ตรวจสอบ FreeRDP ด้วยเครื่องมือวิเคราะห์ PVS-Studio

การตรวจสอบ FreeRDP โดยใช้เครื่องวิเคราะห์ PVS-Studio
FreeRDP เป็นซอฟต์แวร์โอเพนซอร์สที่นำโปรโตคอล Remote Desktop Protocol (RDP) มาใช้ ซึ่งเป็นโปรโตคอลที่พัฒนาโดย Microsoft สำหรับการควบคุมคอมพิวเตอร์จากระยะไกล โครงการนี้รองรับหลายแพลตฟอร์ม รวมถึง Windows, Linux, macOS และแม้แต่ iOS ด้วย Androidโครงการนี้ได้รับการคัดเลือกให้เป็นโครงการแรกในชุดบทความที่เกี่ยวกับการทดสอบไคลเอ็นต์ RDP โดยใช้เครื่องมือวิเคราะห์แบบคงที่ PVS-Studio

บิตของประวัติศาสตร์

โครงการ ฟรี RDP เกิดขึ้นหลังจากที่ Microsoft เปิดข้อกำหนดสำหรับโปรโตคอล RDP ที่เป็นกรรมสิทธิ์ของตน ในเวลานั้น มีไคลเอ็นต์ rdesktop ที่ใช้งานซึ่งอิงตามผลลัพธ์ของ Reverse Engineering

เมื่อมีการนำโปรโตคอลไปใช้ การเพิ่มฟังก์ชันการทำงานใหม่ๆ กลายเป็นเรื่องยากมากขึ้นเนื่องจากสถาปัตยกรรมโปรเจ็กต์ที่มีอยู่ในขณะนั้น การเปลี่ยนแปลงทำให้เกิดความขัดแย้งระหว่างนักพัฒนาซึ่งนำไปสู่การสร้างทางแยกของ rdesktop - FreeRDP การจำหน่ายผลิตภัณฑ์เพิ่มเติมถูกจำกัดโดยใบอนุญาต GPLv2 ซึ่งเป็นผลมาจากการตัดสินใจที่จะคืนใบอนุญาตให้กับ Apache License v2 อย่างไรก็ตาม ไม่ใช่ทุกคนที่ตกลงที่จะเปลี่ยนใบอนุญาตของรหัสของตน ดังนั้นนักพัฒนาจึงตัดสินใจเขียนโครงการใหม่ ส่งผลให้มีฐานรหัสที่ทันสมัย

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับประวัติของโครงการได้ในบล็อกโพสต์อย่างเป็นทางการ: “ประวัติของโครงการ FreeRDP”

ใช้เป็นเครื่องมือในการระบุข้อผิดพลาดและช่องโหว่ที่อาจเกิดขึ้นในโค้ด พีวีเอส-สตูดิโอเป็นเครื่องมือวิเคราะห์โค้ดแบบคงที่สำหรับภาษา C, C++, C# และ Java ซึ่งใช้งานได้บนแพลตฟอร์มต่างๆ Windows, Linux и macOS.

บทความนี้นำเสนอเฉพาะข้อผิดพลาดที่ดูน่าสนใจที่สุดสำหรับฉันเท่านั้น

หน่วยความจำรั่ว

V773 ออกจากฟังก์ชันโดยไม่ปล่อยตัวชี้ 'cwd' หน่วยความจำรั่วเป็นไปได้ สิ่งแวดล้อม.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;
  ....
}

ส่วนนี้คัดลอกมาจากระบบย่อย winpr ซึ่งใช้ตัวห่อ WINAPI สำหรับสิ่งที่ไม่ใช่...Windows ระบบต่างๆ กล่าวคือ มันเป็นเวอร์ชันน้ำหนักเบาของ Wine ตรงนี้คุณจะเห็นการรั่วไหลของหน่วยความจำ: หน่วยความจำที่จัดสรรโดยฟังก์ชัน รับcwdจะถูกปล่อยออกมาเมื่อจัดการกรณีพิเศษเท่านั้น หากต้องการแก้ไขข้อผิดพลาด คุณต้องเพิ่มการโทร ฟรี หลังจาก เมมปี้.

อาร์เรย์อยู่นอกขอบเขต

V557 การโอเวอร์รันอาร์เรย์เป็นไปได้ ค่าของดัชนี 'event->EventHandlerCount' อาจถึง 32 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++;
  }
  ....
}

ตัวอย่างนี้จะเพิ่มองค์ประกอบใหม่ลงในรายการ แม้ว่าจำนวนองค์ประกอบจะถึงจำนวนสูงสุดแล้วก็ตาม นี่ก็เพียงพอแล้วที่จะเปลี่ยนผู้ปฏิบัติงาน <= บน <เพื่อไม่ให้เกินขอบเขตของอาเรย์

พบข้อผิดพลาดประเภทนี้อีก:

  • สามารถโอเวอร์รันอาร์เรย์ V557 ได้ ค่าของดัชนี 'iBitmapFormat' อาจสูงถึง 8. orders.c 2623

ความผิดพลาด

ส่วนที่ 1

V547 นิพจน์ '!pipe->In' จะเป็นเท็จเสมอ 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;
  ....
}

ที่นี่เราเห็นการพิมพ์ผิดทั่วไป: เงื่อนไขที่สองจะตรวจสอบตัวแปรเดียวกันกับเงื่อนไขแรก เป็นไปได้มากว่าข้อผิดพลาดเกิดขึ้นอันเป็นผลมาจากการคัดลอกโค้ดไม่สำเร็จ

ส่วนที่ 2

V760 พบข้อความที่เหมือนกันสองช่วงตึก บล็อกที่สองเริ่มจากบรรทัด 771 tsg.c 770

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);
  ....
}

การพิมพ์ผิดอีกประการหนึ่ง: ความคิดเห็นของโค้ดบอกเป็นนัยว่าเธรดควรจะมา รุ่นรองอย่างไรก็ตาม การอ่านเกิดขึ้นในตัวแปรที่ชื่อ เวอร์ชันหลัก- อย่างไรก็ตาม ฉันไม่คุ้นเคยกับโปรโตคอล ดังนั้นนี่เป็นเพียงการคาดเดาเท่านั้น

ส่วนที่ 3

V524 เป็นเรื่องแปลกที่เนื้อความของฟังก์ชัน 'trio_index_last' เทียบเท่ากับเนื้อความของฟังก์ชัน 'trio_index' โดยสมบูรณ์ ไตรโอสเตร.ซี 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);
}

ตัดสินโดยความคิดเห็นฟังก์ชั่น trio_index ค้นหาอักขระตัวแรกที่ตรงกันในสตริงเมื่อ trio_index_last - สิ่งสุดท้าย. แต่เนื้อความของฟังก์ชันเหล่านี้เหมือนกัน! เป็นไปได้มากว่านี่คือการพิมพ์ผิดและอยู่ในฟังก์ชัน trio_index_last จำเป็นต้องใช้ strrhr แทน ยืด- จากนั้นจะเกิดพฤติกรรมที่คาดหวัง

ส่วนที่ 4

V769 ตัวชี้ 'ข้อมูล' ในนิพจน์เท่ากับ nullptr ค่าผลลัพธ์ของการดำเนินการทางคณิตศาสตร์บนตัวชี้นี้ไม่สมเหตุสมผลและไม่ควรใช้ 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;
  ....
}

ดูเหมือนว่าผู้ดำเนินการปฏิเสธพลาดไปที่นี่โดยไม่ได้ตั้งใจ ! ใกล้ ข้อมูล- เป็นเรื่องแปลกที่เรื่องนี้ไม่มีใครสังเกตเห็น

ส่วนที่ 5

V517 ตรวจพบรูปแบบ 'if (A) {…} else if (A) {…}' มีความเป็นไปได้ที่จะเกิดข้อผิดพลาดเชิงตรรกะ ตรวจสอบบรรทัด: 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);
  }
  ....
}

สองเงื่อนไขสุดท้ายเหมือนกัน: เห็นได้ชัดว่ามีคนลืมตรวจสอบหลังจากคัดลอกแล้ว จากโค้ดจะสังเกตได้ว่าส่วนสุดท้ายทำงานกับค่าสี่ไบต์ ดังนั้นเราจึงสรุปได้ว่าเงื่อนไขสุดท้ายควรเป็น ค่า <= 0x3FFFFFFFF.

พบข้อผิดพลาดประเภทนี้อีก:

  • V517 ตรวจพบรูปแบบ 'if (A) {…} else if (A) {…}' มีความเป็นไปได้ที่จะเกิดข้อผิดพลาดเชิงตรรกะ ตรวจสอบบรรทัด: 169, 173. file.c 169

การตรวจสอบความถูกต้องของข้อมูลอินพุต

ส่วนที่ 1

V547 นิพจน์ 'strcat(target, source) != NULL' เป็นจริงเสมอ ไตรโอสเตร.ซี 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);
}

การตรวจสอบผลลัพธ์ของฟังก์ชันในตัวอย่างนี้ไม่ถูกต้อง การทำงาน สเตรทแคท ส่งคืนตัวชี้ไปยังเวอร์ชันสุดท้ายของสตริงเช่น พารามิเตอร์แรกผ่านไปแล้ว ในกรณีนี้ก็คือ เป้า- แต่ถ้าจะเท่ากัน NULLแสดงว่าสายเกินไปที่จะตรวจสอบเนื่องจากอยู่ในฟังก์ชัน สเตรทแคท มันจะถูกเลื่อนออกไป

ส่วนที่ 2

V547 นิพจน์ 'แคช' เป็นจริงเสมอ 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)
  {
    ....
  }
  ....
}

ในกรณีนี้คือตัวแปร แคช มีการกำหนดที่อยู่ของอาร์เรย์แบบคงที่ glyphCache->glyphCache- ดังนั้นให้ตรวจสอบ ถ้า (แคช) สามารถละเว้นได้

ข้อผิดพลาดในการจัดการทรัพยากร

V1005 ได้รับทรัพยากรโดยใช้ฟังก์ชัน 'CreateFileA' แต่ถูกปล่อยออกมาโดยใช้ฟังก์ชัน 'fclose' ที่เข้ากันไม่ได้ ใบรับรอง 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;
  }
  ....
}

ตัวอธิบายไฟล์ fpสร้างขึ้นโดยการเรียกใช้ฟังก์ชัน สร้างไฟล์ ปิดโดยไม่ได้ตั้งใจตามฟังก์ชัน ปิด จากไลบรารีมาตรฐานไม่ใช่ ปิดแฮนเดิล.

เงื่อนไขเดียวกัน

V581 นิพจน์แบบมีเงื่อนไขของคำสั่ง 'if' ที่อยู่ข้างๆ กันจะเหมือนกัน ตรวจสอบบรรทัด: 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);
  }
  ....
}

ตัวอย่างนี้อาจไม่ใช่จุดบกพร่อง อย่างไรก็ตาม ทั้งสองเงื่อนไขมีข้อความเดียวกัน ซึ่งข้อความหนึ่งน่าจะถูกนำออกได้มากที่สุด

ทำความสะอาดพอยน์เตอร์โมฆะ

V575 ตัวชี้ค่าว่างจะถูกส่งผ่านไปยังฟังก์ชัน 'ฟรี' ตรวจสอบอาร์กิวเมนต์แรก 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);
  ....
}

ในฟังก์ชั่น ฟรี คุณสามารถส่งผ่านตัวชี้ว่างได้และผู้วิเคราะห์จะรู้เรื่องนี้ แต่หากตรวจพบสถานการณ์ที่ตัวชี้ถูกส่งผ่านค่าว่างเสมอ ดังตัวอย่างนี้ จะมีการออกคำเตือน

ตัวชี้ mszGroupsA ในตอนแรกเท่ากัน NULL และไม่ได้เริ่มต้นที่อื่น รหัสสาขาเดียวที่สามารถเตรียมใช้งานตัวชี้ได้ไม่สามารถเข้าถึงได้

มีข้อความอื่นๆ เช่น:

  • V575 ตัวชี้ค่าว่างถูกส่งผ่านไปยังฟังก์ชัน 'ฟรี' ตรวจสอบอาร์กิวเมนต์แรก ใบอนุญาต c 790
  • V575 ตัวชี้ค่าว่างถูกส่งผ่านไปยังฟังก์ชัน 'ฟรี' ตรวจสอบอาร์กิวเมนต์แรก rdpsnd_alsa.c 575

เป็นไปได้มากว่าตัวแปรที่ถูกลืมนั้นเกิดขึ้นในระหว่างกระบวนการรีแฟคเตอร์และสามารถลบออกได้อย่างง่ายดาย

ล้นที่เป็นไปได้

V1028 ล้นที่เป็นไปได้ พิจารณาแคสต์ตัวถูกดำเนินการ ไม่ใช่ผลลัพธ์ 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));
  ....
}

นำผลมาสู่. ยาว ไม่ใช่การป้องกันโอเวอร์โฟลว์ เนื่องจากการคำนวณนั้นเกิดขึ้นโดยใช้ประเภทนั้น int.

การยกเลิกการอ้างอิงตัวชี้ในการเริ่มต้น

V595 ตัวชี้ 'บริบท' ถูกใช้ก่อนที่จะได้รับการตรวจสอบกับ nullptr ตรวจสอบบรรทัด: 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;
  ....
}

นี่คือตัวชี้ สิ่งแวดล้อม ถูกยกเลิกการอ้างอิงระหว่างการเริ่มต้น - ก่อนที่จะตรวจสอบ

พบข้อผิดพลาดอื่นๆ ประเภทนี้:

  • V595 ตัวชี้ 'ntlm' ถูกใช้ก่อนที่จะได้รับการตรวจสอบกับ nullptr ตรวจสอบบรรทัด: 236, 255. ntlm.c 236
  • V595 ตัวชี้ 'บริบท' ถูกใช้ก่อนที่จะได้รับการตรวจสอบกับ nullptr ตรวจสอบบรรทัด: 1003, 1007. rfx.c 1003
  • V595 ตัวชี้ 'rdpei' ถูกใช้ก่อนที่จะได้รับการตรวจสอบกับ nullptr ตรวจสอบบรรทัด: 176, 180. rdpei_main.c 176
  • V595 ตัวชี้ 'gdi' ถูกใช้ก่อนที่จะได้รับการตรวจสอบกับ nullptr ตรวจสอบบรรทัด: 121, 123. xf_gfx.c 121

สภาพที่ไร้ความหมาย

V547 นิพจน์ 'rdp->state >= CONNECTION_STATE_ACTIVE' เป็นจริงเสมอ การเชื่อมต่อ 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;
      }
    ....
  }
  ....
}

สังเกตได้ง่ายว่าเงื่อนไขแรกไม่มีความหมายเนื่องจากมีการกำหนดค่าที่เกี่ยวข้องไว้ก่อนหน้านี้

การแยกวิเคราะห์สตริงไม่ถูกต้อง

V576 รูปแบบไม่ถูกต้อง ลองตรวจสอบอาร์กิวเมนต์จริงตัวที่สามของฟังก์ชัน 'sscanf' คาดว่าจะมีตัวชี้ไปยังประเภท int ที่ไม่ได้ลงนาม พร็อกซี.c 220

V560 ส่วนหนึ่งของนิพจน์เงื่อนไขเป็นจริงเสมอ: (rc >= 0) พร็อกซี.c 222

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

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

เครื่องวิเคราะห์จะออกคำเตือน 2 รายการทันทีสำหรับส่วนนี้ ตัวระบุ %u คาดหวังตัวแปรประเภท int ที่ไม่ได้ลงนามแต่แปรผัน ด้านล่าง มีประเภท int- ต่อไปเราจะเห็นการตรวจสอบที่น่าสงสัย: เงื่อนไขทางด้านขวาไม่สมเหตุสมผลเนื่องจากในตอนแรกมีการเปรียบเทียบกับสิ่งหนึ่ง ฉันไม่รู้ว่าผู้เขียนโค้ดนี้หมายถึงอะไร แต่มีบางอย่างผิดปกติอย่างชัดเจน

การตรวจสอบที่ไม่เป็นระเบียบ

V547 นิพจน์ 'สถานะ == 0x00090314' จะเป็นเท็จเสมอ 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;
  ....
}

เงื่อนไขที่ตรวจสอบจะเป็นเท็จเสมอ เนื่องจากการดำเนินการจะไปถึงเงื่อนไขที่สองเท่านั้น สถานะ == SEC_E_OK- รหัสที่ถูกต้องอาจมีลักษณะดังนี้:

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;
}

ข้อสรุป

ดังนั้นการตรวจสอบโครงการจึงเผยให้เห็นปัญหามากมาย แต่มีเพียงส่วนที่น่าสนใจที่สุดเท่านั้นที่อธิบายไว้ในบทความ ผู้พัฒนาโครงการสามารถตรวจสอบโครงการได้ด้วยตนเองโดยขอรหัสลิขสิทธิ์ชั่วคราวบนเว็บไซต์ พีวีเอส-สตูดิโอ- นอกจากนี้ยังมีผลบวกลวงซึ่งจะช่วยปรับปรุงเครื่องวิเคราะห์ได้ อย่างไรก็ตาม การวิเคราะห์แบบคงที่ถือเป็นสิ่งสำคัญหากคุณต้องการไม่เพียงแต่ปรับปรุงคุณภาพของโค้ดของคุณ แต่ยังช่วยลดเวลาที่ใช้ในการค้นหาข้อผิดพลาด และ PVS-Studio สามารถช่วยคุณได้

การตรวจสอบ FreeRDP โดยใช้เครื่องวิเคราะห์ PVS-Studio

หากคุณต้องการแบ่งปันบทความนี้กับผู้ชมที่พูดภาษาอังกฤษ โปรดใช้ลิงก์การแปล: Sergey Larin ตรวจสอบ FreeRDP ด้วย PVS-Studio

ที่มา: will.com

ซื้อโฮสติ้งที่เชื่อถือได้สำหรับไซต์ที่มีการป้องกัน DDoS เซิร์ฟเวอร์ VPS VDS 🔥 ซื้อบริการเว็บโฮสติ้งที่เชื่อถือได้ พร้อมระบบป้องกัน DDoS และเซิร์ฟเวอร์ VPS/VDS | ProHoster