Bi karanîna analîzera PVS-Studio FreeRDP-ê kontrol bikin

Bi karanîna analîzera PVS-Studio FreeRDP-ê kontrol bikin
FreeRDP pêkanînek çavkaniya vekirî ya Protokola Desktopê ya Ji Dûr (RDP) ye, protokolek ku ji hêla Microsoft ve ji bo kontrola komputerê ya ji dûr ve hatî pêşve xistin. Proje gelek platforman piştgirî dike, di nav de Windows, Linux, macOS û tewra iOS jî bi AndroidEv proje di rêze gotaran de wekî ya yekem hate hilbijartin ku ji bo ceribandina xerîdarên RDP-ê bi karanîna analîzkera statîk a PVS-Studio ve hatî veqetandin.

Hinek dîrok

Projeyê FreeRDP Piştî ku Microsoft ji bo protokola xweya RDP-ya xwedan taybetmendî vekir derket holê. Di wê demê de, xerîdarek rdesktop hebû, ku pêkanîna wê li ser bingeha encamên Endezyariya Reverse bû.

Her ku protokol hate bicîh kirin, ji ber mîmariya projeya heyî ya wê demê zêdekirina fonksiyonên nû dijwartir bû. Guhertinên di wê de bû sedema nakokî di navbera pêşdebiran de, ku bû sedema afirandina forkek rdesktop - FreeRDP. Belavkirina din a hilberê ji hêla lîsansa GPLv2 ve hate sînorkirin, di encamê de biryar hate girtin ku wê ji nû ve lîsansa Apache-ya v2-ê were veguheztin. Lêbelê, ne her kes razî bû ku lîsansa koda xwe biguhezîne, ji ber vê yekê pêşdebiran biryar da ku projeyê ji nû ve binivîsin, û di encamê de bingehek kodek nûjen derket.

Hûn dikarin li ser dîroka projeyê di posta blogê ya fermî de bêtir bixwînin: "Dîroka projeya FreeRDP".

Ji bo naskirina xeletî û qelsiyên potansiyel ên di kodê de wekî amûrek tê bikar anîn. PVS StudioEw analîzkerek kodê statîk e ji bo C, C++, C# û Java, ku li ser platforman peyda dibe. Windows, Linux и macOS.

Gotar tenê wan xeletiyên ku ji min re balkêş xuya bûn pêşkêşî dike.

Memory leak

V773 Fonksiyon bêyî berdana nîşana 'cwd' derket. Leza bîranînê gengaz e. jîngeh.c 84

DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer)
{
  char* cwd;
  ....
  cwd = getcwd(NULL, 0);
  ....
  if (lpBuffer == NULL)
  {
    free(cwd);
    return 0;
  }

  if ((length + 1) > nBufferLength)
  {
    free(cwd);
    return (DWORD) (length + 1);
  }

  memcpy(lpBuffer, cwd, length + 1);
  return length;
  ....
}

Ev perçe ji jêrpergala winpr hatiye girtin, ku pêça WINAPI ji bo ne- bicîh tîne.Windows sîsteman, ango ew analogek sivik a Wine ye. Li vir hûn dikarin rijandinekê bibînin: bîra ku ji hêla fonksiyonê ve hatî veqetandin getcwd, tenê dema ku bi dozên taybetî re mijûl dibe tê berdan. Ji bo rastkirina xeletiyê hûn hewce ne ku bangek lê zêde bikin belaş после memcpy.

Array derveyî sînor

V557 Zêdekirina array gengaz e. Nirxa nîşaneya 'bûyer->EventHandlerCount' dikare bigihêje 32'an. PubSub.c 117

#define MAX_EVENT_HANDLERS  32

struct _wEventType
{
  ....
  int EventHandlerCount;
  pEventHandler EventHandlers[MAX_EVENT_HANDLERS];
};

int PubSub_Subscribe(wPubSub* pubSub, const char* EventName,
      pEventHandler EventHandler)
{
  ....
  if (event->EventHandlerCount <= MAX_EVENT_HANDLERS)
  {
    event->EventHandlers[event->EventHandlerCount] = EventHandler;
    event->EventHandlerCount++;
  }
  ....
}

Ev nimûne hêmanek nû li lîsteyê zêde dike ku hejmara hêmanan gihîştibe herî zêde. Li vir ew bes e ku operator biguhezîne <= li ser <, da ku ji sînorên array dernekevin.

Çewtiyek din a bi vî rengî hate dîtin:

  • Zêdekirina Array V557 gengaz e. Nirxa nîşana 'iBitmapFormat' dikare bigihîje 8. ferman dike.c 2623

Çewtiya tîpan

Parçeya 1

V547 Îfadeya '!pipe->In' her dem xelet e. MessagePipe.c 63

wMessagePipe* MessagePipe_New()
{
  ....
  pipe->In = MessageQueue_New(NULL);
  if (!pipe->In)
    goto error_in;

  pipe->Out = MessageQueue_New(NULL);
  if (!pipe->In) // <=
    goto error_out;
  ....
}

Li vir em tîpek hevpar dibînin: şerta duyemîn heman guherbarê ya yekem kontrol dike. Bi îhtîmalek mezin, xeletî di encama kopîkirina kodê ya neserkeftî de xuya bû.

Parçeya 2

V760 Du blokên nivîsa wekhev hatin dîtin. Bloka duyemîn ji rêza 771. tsg.c 770 dest pê dike

typedef struct _TSG_PACKET_VERSIONCAPS
{
  ....
  UINT16 majorVersion;
  UINT16 minorVersion;
  ....
} TSG_PACKET_VERSIONCAPS, *PTSG_PACKET_VERSIONCAPS;

static BOOL TsProxyCreateTunnelReadResponse(....)
{
  ....
  PTSG_PACKET_VERSIONCAPS versionCaps = NULL;
  ....
  /* MajorVersion (2 bytes) */
  Stream_Read_UINT16(pdu->s, versionCaps->majorVersion);
  /* MinorVersion (2 bytes) */
  Stream_Read_UINT16(pdu->s, versionCaps->majorVersion);
  ....
}

Tîpayek din: şîroveya kodê tê vê wateyê ku divê mijar were minorVersion, lebê, xwendin nav guherbareke bi navê pêk tê majorVersion. Lêbelê, ez bi protokolê ne nas im, ji ber vê yekê ev tenê texmînek e.

Parçeya 3

V524 Ecêb e ku laşê fonksiyona 'trio_index_last' bi tevahî bi fonksiyona 'trio_index' re hevwate ye. triostr.c 933

/**
   Find first occurrence of a character in a string.
   ....
 */
TRIO_PUBLIC_STRING char *
trio_index
TRIO_ARGS2((string, character),
     TRIO_CONST char *string,
     int character)
{
  assert(string);
  return strchr(string, character);
}

/**
   Find last occurrence of a character in a string.
   ....
 */
TRIO_PUBLIC_STRING char *
trio_index_last
TRIO_ARGS2((string, character),
     TRIO_CONST char *string,
     int character)
{
  assert(string);
  return strchr(string, character);
}

Dadbarkirina şîroveyê, fonksiyonê trio_index Dema ku di rêzikê de hevahenga karaktera yekem dibîne trio_index_last - tişta dawî. Lê laşên van fonksiyonan yek in! Bi îhtîmaleke mezin ev xeletiyek tîpî ye, û di fonksiyonê de ye trio_index_last pêdivî ye ku bikar bînin strhrchr li gorî strchr. Wê hingê tevger dê were hêvî kirin.

Parçeya 4

V769 Nîşaneya 'data' di îfadeyê de wekî nullptr ye. Nirxa encam a operasyonên hejmarî yên li ser vê nîşankerê bêwate ye û divê neyê bikar anîn. nsc_encode.c 124

static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context,
                                      const BYTE* data,
                                      UINT32 scanline)
{
  ....
  if (!context || data || (scanline == 0))
    return FALSE;
  ....
  src = data + (context->height - 1 - y) * scanline;
  ....
}

Wusa dixuye ku operatorê înkarê bi xeletî li vir winda bûye ! Nêz jimare. Ecêb e ku ev yek ji nedîtî ve çû.

Parçeya 5

V517 Bikaranîna nimûneya 'heke (A) {…} din eger (A) {…}' hat dîtin. Îhtîmala hebûna xeletiya mantiqî heye. Rêzan kontrol bikin: 213, 222. rdpei_common.c 213

BOOL rdpei_write_4byte_unsigned(wStream* s, UINT32 value)
{
  BYTE byte;

  if (value <= 0x3F)
  {
    ....
  }
  else if (value <= 0x3FFF)
  {
    ....
  }
  else if (value <= 0x3FFFFF)
  {
    byte = (value >> 16) & 0x3F;
    Stream_Write_UINT8(s, byte | 0x80);
    byte = (value >> 8) & 0xFF;
    Stream_Write_UINT8(s, byte);
    byte = (value & 0xFF);
    Stream_Write_UINT8(s, byte);
  }
  else if (value <= 0x3FFFFF)
  {
    byte = (value >> 24) & 0x3F;
    Stream_Write_UINT8(s, byte | 0xC0);
    byte = (value >> 16) & 0xFF;
    Stream_Write_UINT8(s, byte);
    byte = (value >> 8) & 0xFF;
    Stream_Write_UINT8(s, byte);
    byte = (value & 0xFF);
    Stream_Write_UINT8(s, byte);
  }
  ....
}

Du şertên paşîn yek in: xuya ye ku kesek piştî kopîkirinê ji bîr kiriye ku wan kontrol bike. Ji kodê tê xuyang kirin ku beşa paşîn bi nirxên çar-byte dixebite, ji ber vê yekê em dikarin texmîn bikin ku şerta paşîn divê nirx <= 0x3FFFFFFFFFF.

Çewtiyek din a bi vî rengî hate dîtin:

  • V517 Bikaranîna nimûneya 'heke (A) {…} din eger (A) {…}' hat dîtin. Îhtîmala hebûna xeletiya mantiqî heye. Rêzan kontrol bikin: 169, 173. pel.c 169

Rastkirina daneyên têketinê

Parçeya 1

V547 Gotina 'strcat(hedef, çavkanî) != NULL' her dem rast e. triostr.c 425

TRIO_PUBLIC_STRING int
trio_append
TRIO_ARGS2((target, source),
     char *target,
     TRIO_CONST char *source)
{
  assert(target);
  assert(source);
  
  return (strcat(target, source) != NULL);
}

Kontrolkirina encama fonksiyonê ya di vê nimûneyê de nerast e. Karkirin strcat nîşankerek vedigerîne guhertoya dawî ya rêzê, ango. pîvana yekem derbas bû. Di vê rewşê de ew e armanc. Lêbelê, heke ew wekhev be NULL, hingê ew pir dereng e ku meriv wê kontrol bike, ji ber ku di fonksiyonê de ye strcat wê bê referans kirin.

Parçeya 2

V547 Gotina 'cache' her dem rast e. glyph.c 730

typedef struct rdp_glyph_cache rdpGlyphCache;

struct rdp_glyph_cache
{
  ....
  GLYPH_CACHE glyphCache[10];
  ....
};

void glyph_cache_free(rdpGlyphCache* glyphCache)
{
  ....
  GLYPH_CACHE* cache = glyphCache->glyphCache;

  if (cache)
  {
    ....
  }
  ....
}

Di vê rewşê de guherbar cache navnîşana rêzek statîk tê destnîşankirin glyphCache-> glyphCache. Bi vî awayî, kontrol bikin heke (cache) dikare were derxistin.

Çewtiya rêveberiya çavkaniyê

V1005 Çavkanî bi karanîna fonksiyona 'CreateFileA' hate bidestxistin lê bi karanîna fonksiyona 'fclose' ya nehevgirtî hate berdan. sertîfîka.c 447

BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
                              rdpCertificateData* certificate_data)
{
  HANDLE fp;
  ....
  fp = CreateFileA(certificate_store->file, GENERIC_READ | GENERIC_WRITE, 0,
                   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  ....
  if (size < 1)
  {
    CloseHandle(fp);
    return FALSE;
  }
  ....
  if (!data)
  {
    fclose(fp);
    return FALSE;
  }
  ....
}

Danasîna pelê fp, ji hêla bangek fonksiyonê ve hatî afirandin CreateFile bi xeletî ji hêla fonksiyonê ve hatî girtin fclose ji pirtûkxaneya standard, ne CloseHandle.

Heman şert û merc

V581 Gotinên şertî yên bêjeyên 'eger' li kêleka hev in. Rêzan kontrol bikin: 269, 283. ndr_structure.c 283

void NdrComplexStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
      unsigned char* pMemory, PFORMAT_STRING pFormat)
{
  ....
  if (conformant_array_description)
  {
    ULONG size;
    unsigned char array_type;
    array_type = conformant_array_description[0];
    size = NdrComplexStructMemberSize(pStubMsg, pFormat);
    WLog_ERR(TAG, "warning: NdrComplexStructBufferSize array_type: "
      "0x%02X unimplemented", array_type);
    NdrpComputeConformance(pStubMsg, pMemory + size,
      conformant_array_description);
    NdrpComputeVariance(pStubMsg, pMemory + size,
      conformant_array_description);
    MaxCount = pStubMsg->MaxCount;
    ActualCount = pStubMsg->ActualCount;
    Offset = pStubMsg->Offset;
  }

  if (conformant_array_description)
  {
    unsigned char array_type;
    array_type = conformant_array_description[0];
    pStubMsg->MaxCount = MaxCount;
    pStubMsg->ActualCount = ActualCount;
    pStubMsg->Offset = Offset;
    WLog_ERR(TAG, "warning: NdrComplexStructBufferSize array_type: "
      "0x%02X unimplemented", array_type);
  }
  ....
}

Dibe ku ev nimûne ne xeletiyek be. Lêbelê, her du şert heman peyaman digirin, yek ji wan bi îhtîmalek mezin dikare were rakirin.

Paqijkirina nîşangirên null

V575 Nîşana null derbasî fonksiyona 'belaş' dibe. Argumana yekem kontrol bikin. smartcard_pcsc.c 875

WINSCARDAPI LONG WINAPI PCSC_SCardListReadersW(
  SCARDCONTEXT hContext,
  LPCWSTR mszGroups,
  LPWSTR mszReaders,
  LPDWORD pcchReaders)
{
  LPSTR mszGroupsA = NULL;
  ....
  mszGroups = NULL; /* mszGroups is not supported by pcsc-lite */

  if (mszGroups)
    ConvertFromUnicode(CP_UTF8,0, mszGroups, -1, 
                       (char**) &mszGroupsA, 0,
                       NULL, NULL);

  status = PCSC_SCardListReaders_Internal(hContext, mszGroupsA,
                                          (LPSTR) &mszReadersA,
                                          pcchReaders);

  if (status == SCARD_S_SUCCESS)
  {
    ....
  }

  free(mszGroupsA);
  ....
}

Ji bo fonksiyonê belaş hûn dikarin nîşanek betal derbas bikin û analîzer li ser wê dizane. Lê heke rewşek were dîtin ku tê de nîşanker her gav betal tê derbas kirin, wekî di vê perçeyê de, dê hişyariyek were dayîn.

Pointer mszGroupsA di destpêkê de wekhev NULL û li cîhek din nayê destpêkirin. Yekane şaxê kodê ku nîşanker dikare were destpêkirin negihîştî ye.

Peyamên din jî hebûn:

  • V575 Nîşana null derbasî fonksiyona 'belaş' dibe. Argumana yekem kontrol bikin. license.c 790
  • V575 Nîşana null derbasî fonksiyona 'belaş' dibe. Argumana yekem kontrol bikin. rdpsnd_alsa.c 575

Bi îhtimaleke mezin, guhêrbarên weha yên jibîrkirî di dema pêvajoya refaktorkirinê de derdikevin û bi hêsanî têne rakirin.

Zêdebûna gengaz

V1028 Zêdebûna gengaz. Hilberîna operandan bihesibînin, ne encamê. makecert.c 1087

// openssl/x509.h
ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj);

struct _MAKECERT_CONTEXT
{
  ....
  int duration_years;
  int duration_months;
};

typedef struct _MAKECERT_CONTEXT MAKECERT_CONTEXT;

int makecert_context_process(MAKECERT_CONTEXT* context, ....)
{
  ....
  if (context->duration_months)
    X509_gmtime_adj(after, (long)(60 * 60 * 24 * 31 *
      context->duration_months));
  else if (context->duration_years)
    X509_gmtime_adj(after, (long)(60 * 60 * 24 * 365 *
      context->duration_years));
  ....
}

Encamê tîne dirêj ne parastina zêdebûnê ye, ji ber ku hesab bixwe bi karanîna celebê pêk tê int.

Di destpêkê de dereferansa nîşankerê

V595 Nîşaneya 'context' beriya ku li dijî nullptr were verast kirin hate bikar anîn. Rêzan kontrol bikin: 746, 748. gfx.c 746

static UINT gdi_SurfaceCommand(RdpgfxClientContext* context,
                               const RDPGFX_SURFACE_COMMAND* cmd)
{
  ....
  rdpGdi* gdi = (rdpGdi*) context->custom;

  if (!context || !cmd)
    return ERROR_INVALID_PARAMETER;
  ....
}

Li vir nîşanker e hevgirêk di dema destpêkirinê de - berî ku were kontrol kirin - tê betal kirin.

Çewtiyên din ên bi vî rengî hatin dîtin:

  • V595 Nîşana 'ntlm' beriya ku li dijî nullptr were piştrast kirin hate bikar anîn. Rêzan kontrol bikin: 236, 255. ntlm.c 236
  • V595 Nîşaneya 'context' berî ku li dijî nullptr were piştrast kirin hate bikar anîn. Rêzan kontrol bikin: 1003, 1007. rfx.c 1003
  • V595 Nîşankera 'rdpei' berî ku li dijî nullptr were pejirandin hate bikar anîn. Rêzan kontrol bikin: 176, 180. rdpei_main.c 176
  • V595 Nîşankera 'gdi' berî ku li dijî nullptr were pejirandin hate bikar anîn. Rêzan kontrol bikin: 121, 123. xf_gfx.c 121

Rewşa bê wate

V547 Gotina 'rdp->state >= CONNECTION_STATE_ACTIVE' her dem rast e. girêdan.c 1489

int rdp_server_transition_to_state(rdpRdp* rdp, int state)
{
  ....
  switch (state)
  {
    ....
    case CONNECTION_STATE_ACTIVE:
      rdp->state = CONNECTION_STATE_ACTIVE;          // <=
      ....
      if (rdp->state >= CONNECTION_STATE_ACTIVE)     // <=
      {
        IFCALLRET(client->Activate, client->activated, client);

        if (!client->activated)
          return -1;
      }
    ....
  }
  ....
}

Hêsan e ku meriv bibîne ku şerta yekem ji ber nirxa têkildar a ku berê hatî destnîşan kirin bêwate ye.

Parskirina rêzika çewt

V576 Forma çewt. Bifikirin ku argumana sêyemîn a rastîn a fonksiyona 'sscanf' kontrol bikin. Nîşanek ji bo celebê int-ê ya bê îmze tê çaverê kirin. proxy.c 220

V560 Parçeyek bêjeya şertî her dem rast e: (rc >= 0). proxy.c 222

static BOOL check_no_proxy(....)
{
  ....
  int sub;
  int rc = sscanf(range, "%u", &sub);

  if ((rc == 1) && (rc >= 0))
  {
    ....
  }
  ....
}

Analîzker tavilê ji bo vê perçeyê 2 hişyariyan dide. Specifier %u guherbareke cureyê hêvî dike unsigned int, lê guherbar sub tîp heye int. Dûv re em kontrolek gumanbar dibînin: şerta li rastê ne wate ye, ji ber ku di destpêkê de bi yekê re berhevokek heye. Nizanim mebesta nivîskarê vê kodê çi ye, lê tiştek li vir eşkere xelet e.

Kontrolên ji rêzê

V547 Îfadeya 'status == 0x00090314' her dem xelet e. ntlm.c 299

BOOL ntlm_authenticate(rdpNtlm* ntlm, BOOL* pbContinueNeeded)
{
  ....
  if (status != SEC_E_OK)
  {
    ....
    return FALSE;
  }

  if (status == SEC_I_COMPLETE_NEEDED)            // <=
    status = SEC_E_OK;
  else if (status == SEC_I_COMPLETE_AND_CONTINUE) // <=
    status = SEC_I_CONTINUE_NEEDED;
  ....
}

Mercên kontrolkirî dê her gav derew bin, ji ber ku darvekirin dê tenê bigihîje rewşa duyemîn heke statû == SEC_E_OK. Dibe ku koda rast bi vî rengî xuya bike:

if (status == SEC_I_COMPLETE_NEEDED)
  status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
  status = SEC_I_CONTINUE_NEEDED;
else if (status != SEC_E_OK)
{
  ....
  return FALSE;
}

encamê

Bi vî rengî, kontrolkirina projeyê gelek pirsgirêk eşkere kir, lê tenê beşa herî balkêş a wan di gotarê de hate vegotin. Pêşdebirên projeyê dikarin bi daxwazkirina mifteyek destûrnameyek demkî ya li ser malperê, projeyê bi xwe kontrol bikin PVS Studio. Di heman demê de pozîtîfên derewîn jî hebûn, xebata ku dê li ser baştirkirina analîzkerê bibe alîkar. Lêbelê, analîza statîk girîng e heke hûn dixwazin ne tenê qalîteya koda xwe baştir bikin, lê di heman demê de dema ku ji bo dîtina xeletiyan derbas dibe jî kêm bikin, û PVS-Studio dikare bi vê yekê re bibe alîkar.

Bi karanîna analîzera PVS-Studio FreeRDP-ê kontrol bikin

Heke hûn dixwazin vê gotarê bi temaşevanên îngilîzîaxêv re parve bikin, ji kerema xwe lînka wergerê bikar bînin: Sergey Larin. Kontrolkirina FreeRDP bi PVS-Studio

Source: www.habr.com

Ji bo malperên bi parastina DDoS, serverên VPS VDS mêvandariya pêbawer bikirin 🔥 Hostinga malperê ya pêbawer bi parastina DDoS, serverên VPS VDS bikirin | ProHoster