Espressif IoT Development Framework: 71 Shots in the Leg

0790_Espressif_IoT_Development_Framework_ru / image1.png

One of our readers drew our attention to the Espressif IoT Development Framework. He found an error in the project's code and asked if the PVS-Studio static analyzer could find it. The analyzer cannot find this error yet, but it has found many others. Based on this story and the errors found, we decided to write a classic article about checking an open source project. Enjoy exploring why IoT devices can shoot you in the foot.







Hardware and software systems



The father of the C ++ language, Bjarne Stroustrup, once said :







"C" makes it very easy to shoot yourself in the foot. It is more difficult to do it in "C ++", but when you do it, it takes off the whole leg.

, . , - , , .







, Espressif IoT Development Framework, - , . . MISRA AUTOSAR. , .







Espressif IoT Development Framework ( GitHub: esp-idf). :







ESP-IDF is Espressif's official IoT Development Framework for the ESP32 and ESP32-S series of SoCs. It provides a self-sufficient SDK for any generic application development on these platforms, using programming languages such as C and C++. ESP-IDF currently powers millions of devices in the field, and enables building a variety of network-connected products, ranging from simple light bulbs and toys to big appliances and industrial devices.

, , . , , . , . , /, .









, . (Hardcore IoT fullstack dev & CTO), . Espressif IoT Development Framework , PVS-Studio. , PVS-Studio , .







mdns.c:







mdns_txt_linked_item_t * txt = service->txt;
while (txt) {
  data_len += 2 + strlen(service->txt->key) + strlen(service->txt->value);
  txt = txt->next;
}
      
      





, , . , .







:







data_len += 2 + strlen(txt->key) + strlen(txt->value);
      
      





, PVS-Studio . . , . PVS-Studio, , , :).







, , . , PVS-Studio. , , PVS-Studio. . . TODO .







, , PVS-Studio: " ESP-IDF: MDNS, Wireshark ". : Spurious MDNS collision detection (IDFGH-4263).







. . , PVS-Studio :).







. , " ". . getting_started\hello_world. , . , . , , 71 , — :).







, . , , , . .







, , , , . , . , , , , .







, /



, Espressif IoT Development Framework, , . / . .







(#ifdef) . . , , .







PVS-Studio: V547 Expression 'ret != 0' is always true. esp_hidd.c 45







esp_err_t esp_hidd_dev_init(....)
{
  esp_err_t ret = ESP_OK;
  ....
  switch (transport) {
#if CONFIG_GATTS_ENABLE
  case ESP_HID_TRANSPORT_BLE:
    ret = esp_ble_hidd_dev_init(dev, config, callback);
    break;
#endif /* CONFIG_GATTS_ENABLE */
  default:
    ret = ESP_FAIL;
    break;
  }

  if (ret != ESP_OK) {
    free(dev);
    return ret;
  }
  ....
}
      
      





, CONFIG_GATTS_ENABLE . , :







esp_err_t ret = ESP_OK;
....
switch (transport) {
default:
  ret = ESP_FAIL;
  break;
}
if (ret != ESP_OK) {
      
      





, . , , , . , . :).







. assert-. , . PVS-Studio: V547 Expression 'sntp_pcb != NULL' is always true. sntp.c 664







#define LWIP_PLATFORM_ASSERT(x) do \
  {printf("Assertion \"%s\" failed at line %d in %s\n", \
    x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)

#ifndef LWIP_NOASSERT
#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
  LWIP_PLATFORM_ASSERT(message); }} while(0)
#else  /* LWIP_NOASSERT */
#define LWIP_ASSERT(message, assertion)
#endif /* LWIP_NOASSERT */

sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
if (sntp_pcb != NULL) {
      
      





, LWIP_ASSERT (. abort), sntp_pcb . PVS-Studio , (sntp_pcb != NULL) .







, . , "". . , , . .







, . , , . , . . , .. . " legacy ".







Security



, , , security. , , , .







CWE (Common Weakness Enumeration). PVS-Studio CWE ID . CWE ID.







" PVS-Studio ".







N1;



PVS-Studio: V764 Possible incorrect order of arguments passed to 'crypto_generichash_blake2b__init_salt_personal' function: 'salt' and 'personal'. blake2b-ref.c 457







int blake2b_init_salt_personal(blake2b_state *S, const uint8_t outlen,
                               const void *personal, const void *salt);

int
blake2b_salt_personal(uint8_t *out, const void *in, const void *key,
                      const uint8_t outlen, const uint64_t inlen,
                      uint8_t keylen, const void *salt, const void *personal)
{
  ....
  if (blake2b_init_salt_personal(S, outlen, salt, personal) < 0)
    abort();
  ....
}
      
      





blake2b_init_salt_personal personal salt. , , , , . , - , .







CWE CWE-683: Function Call With Incorrect Order of Arguments.







N2;



PVS-Studio: V642 Saving the 'memcmp' function result inside the 'unsigned char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. mbc_tcp_master.c 387







static esp_err_t mbc_tcp_master_set_request(
  char* name, mb_param_mode_t mode, mb_param_request_t* request,
  mb_parameter_descriptor_t* reg_data)
{
  ....
  // Compare the name of parameter with parameter key from table
  uint8_t comp_result = memcmp((const char*)name,
                               (const char*)reg_ptr->param_key,
                               (size_t)param_key_len);
  if (comp_result == 0) {
  ....
}
      
      





memcmp — . , , : CVE-2012-2122. , , V642.







, memset 1 -1. , , 1024. , uint8_t 0.







CWE CWE-197: Numeric Truncation Error.







N3 — N20;



PVS-Studio: V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 854







#ifndef os_memset
#define os_memset(s, c, n) memset(s, c, n)
#endif

static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
       unsigned int hash_len)
{
  u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
  const char *info = "first intermediate key";
  int res;

  /* k1 = HKDF(<>, "first intermediate key", M.x) */

  /* HKDF-Extract(<>, M.x) */
  os_memset(salt, 0, hash_len);
  if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
    return -1;
  wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
      prk, hash_len);

  /* HKDF-Expand(PRK, info, L) */
  res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);

  os_memset(prk, 0, hash_len);             // <=
  if (res < 0)
    return -1;

  wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
                  k1, hash_len);
  return 0;
}
      
      





. memset, , . , - . " ".







CWE CWE-14: Compiler Removal of Code to Clear Buffers.







:







  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 883
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 942
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'psk' buffer. The memset_s() function should be used to erase the private data. dpp.c 3939
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 5729
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'Nx' buffer. The memset_s() function should be used to erase the private data. dpp.c 5934
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'val' buffer. The memset_s() function should be used to erase the private data. sae.c 155
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'keyseed' buffer. The memset_s() function should be used to erase the private data. sae.c 834
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'keys' buffer. The memset_s() function should be used to erase the private data. sae.c 838
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'pkey' buffer. The memset_s() function should be used to erase the private data. des-internal.c 422
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'ek' buffer. The memset_s() function should be used to erase the private data. des-internal.c 423
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'finalcount' buffer. The memset_s() function should be used to erase the private data. sha1-internal.c 358
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'A_MD5' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 95
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'P_MD5' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 96
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'A_SHA1' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 97
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'P_SHA1' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 98
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'T' buffer. The memset_s() function should be used to erase the private data. sha256-kdf.c 85
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'hash' buffer. The memset_s() function should be used to erase the private data. sha256-prf.c 105


N21;



PVS-Studio: V575 The null pointer is passed into 'free' function. Inspect the first argument. sae.c 1185







static int sae_parse_password_identifier(struct sae_data *sae,
           const u8 *pos, const u8 *end)
{
  wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
        pos, end - pos);
  if (!sae_is_password_id_elem(pos, end)) {
    if (sae->tmp->pw_id) {
      wpa_printf(MSG_DEBUG,
           "SAE: No Password Identifier included, but expected one (%s)",
           sae->tmp->pw_id);
      return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
    }
    os_free(sae->tmp->pw_id);
    sae->tmp->pw_id = NULL;
    return WLAN_STATUS_SUCCESS; /* No Password Identifier */
  }
  ....
}
      
      





- pw_id , . , , . , NULL. . , . , :







if (!sae_is_password_id_elem(pos, end)) {
  if (sae->tmp->pw_id) {
    wpa_printf(MSG_DEBUG,
         "SAE: No Password Identifier included, but expected one (%s)",
         sae->tmp->pw_id);
    os_free(sae->tmp->pw_id);
    sae->tmp->pw_id = NULL;
    return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
  }
  return WLAN_STATUS_SUCCESS; /* No Password Identifier */
}
      
      





-, , . -, - .







CWE, CWE-628: Function Call with Incorrectly Specified Arguments. PVS-Studio, , , - .







N22, N23;



PVS-Studio: V614 Uninitialized buffer 'hex' used. Consider checking the second actual argument of the 'memcpy' function. wps_registrar.c 1657







int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
  ....
  } else if (wps->use_psk_key && wps->wps->psk_set) {
    char hex[65];
    wpa_printf(MSG_DEBUG,  "WPS: Use PSK format for Network Key");
    os_memcpy(wps->cred.key, hex, 32 * 2);
    wps->cred.key_len = 32 * 2;
  } else if (wps->wps->network_key) {
  ....
}
      
      





hex - . — . , - , .







.







CWE, CWE-457: Use of Uninitialized Variable.







: V614 Uninitialized buffer 'hex' used. Consider checking the second actual argument of the 'memcpy' function. wps_registrar.c 1678







Copy-Paste



N24; Copy-Paste



PVS-Studio: V523 The 'then' statement is equivalent to the 'else' statement. timer.c 292







esp_err_t timer_isr_register(....)
{
  ....
  if ((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) {
    intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;
  } else {
    intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;
  }
  ....
}
      
      





, , - . , , intr_source .







. , . , , (.. "todo-"). .







N25;



PVS-Studio: V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. esp_tls_mbedtls.c 446







esp_err_t set_client_config(....)
{
 ....
 if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0))
 {
   ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);
   ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
   return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED;
 }
 ....
}
      
      





, . :







TEMP = mbedtls_ssl_conf_alpn_protocols(....) != 0;
if ((ret = TEMP))
  PRINT(...., -ret);
      
      





, , , . , ret. ret 0 1. , - , (-1).







- , . :







if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos)) != 0)
      
      





:







ret = mbedtls_ssl_conf_alpn_protocols(....);
if (ret != 0)
  PRINT(...., -ret);
      
      





.







N26; MP_MEM MP_YES



V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. libtommath.h 1660







. .







#define MP_OKAY       0   /* ok result */
#define MP_MEM        -2  /* out of mem */
#define MP_VAL        -3  /* invalid input */
#define MP_YES        1   /* yes response */
      
      





, mp_init_multi, MP_OKAY MP_MEM:







static int mp_init_multi(mp_int *mp, ...);
      
      





:







static int
mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
{
  ....
  /* init our temps */
  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {
     return res;
  }
  ....
}
      
      





:







if ((res = mp_init_multi(....) != MP_OKAY))
      
      





. :







TEMP = (mp_init_multi(....) != MP_OKAY);
      
      





TEMP 0 1. MB_OKAY MP_YES.







:







if ((res = TEMP))
   return res;
      
      





? MP_MEM (-2) MB_YES (1). , .







N27;



PVS-Studio: V595 The 'outbuf' pointer was utilized before it was verified against nullptr. Check lines: 374, 381. protocomm.c 374







static int protocomm_version_handler(uint32_t session_id,
                                     const uint8_t *inbuf, ssize_t inlen,
                                     uint8_t **outbuf, ssize_t *outlen,
                                     void *priv_data)
{
    protocomm_t *pc = (protocomm_t *) priv_data;
    if (!pc->ver) {
        *outlen = 0;
        *outbuf = NULL;                                  // <=
        return ESP_OK;
    }

    /* Output is a non null terminated string with length specified */
    *outlen = strlen(pc->ver);
    *outbuf = malloc(*outlen);                           // <=
    if (outbuf == NULL) {                                // <=
        ESP_LOGE(TAG, "Failed to allocate memory for version response");
        return ESP_ERR_NO_MEM;
    }

    memcpy(*outbuf, pc->ver, *outlen);
    return ESP_OK;
}
      
      





, , . .







pc->ver , , outbuf:







*outbuf = NULL;
      
      





:







*outbuf = malloc(*outlen);
      
      





, :







if (outbuf == NULL)
      
      





, , , . , , , . (*).







:







*outbuf = malloc(*outlen);
if (*outbuf == NULL) {
  ESP_LOGE(TAG, "Failed to allocate memory for version response");
  return ESP_ERR_NO_MEM;
}
      
      





N28;



PVS-Studio: V519 The 'usRegCount' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 186, 187. mbfuncholding.c 187







eMBException
eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
  ....
  USHORT          usRegCount;
  ....
  usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
  usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
  ....
}
      
      





Copy-Paste. , . :







usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8 );
usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1] );
      
      





, , =, |=.









N29 — N31; (Rare)



PVS-Studio: V547 Expression is always false. linenoise.c 256







static int getColumns(void) {
  ....
  /* Restore position. */
  if (cols > start) {
    char seq[32];
    snprintf(seq,32,"\x1b[%dD",cols-start);
    if (fwrite(seq, 1, strlen(seq), stdout) == -1) {
      /* Can't recover... */
    }
    flushWrite();
  }
  ....
}
      
      





, . , . , :). , , , .







, fwrite -1. , fwrite size_t:







size_t fwrite( const void *restrict buffer, size_t size, size_t count,
               FILE *restrict stream );
      
      





:







The number of objects written successfully, which may be less than count if an error occurs.



If size or count is zero, fwrite returns zero and performs no other action.

, .







:







  • V547 Expression is always false. linenoise.c 481
  • V547 Expression is always false. linenoise.c 569


N32, N33; (Medium)



PVS-Studio: V547 Expression is always false. linenoise.c 596







int linenoiseEditInsert(struct linenoiseState *l, char c) {
  ....
  if (fwrite(&c,1,1,stdout) == -1) return -1;
  ....
}
      
      





, , . , linenoiseEditInsert -1. , fwrite -1. , - .







: V547 Expression is always false. linenoise.c 742.







N34; (Well Done)



PVS-Studio: V547 Expression is always false. linenoise.c 828







static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
  ....
  while(1) {
    ....
    if (fread(seq+2, 1, 1, stdin) == -1) break;
    ....
  }
  ....
}
      
      





, , fwrite, fread -1.







size_t fread( void *restrict buffer, size_t size, size_t count,
              FILE *restrict stream );
      
      





Return value



Number of objects read successfully, which may be less than count if an error or end-of-file condition occurs.



If size or count is zero, fread returns zero and performs no other action.



fread does not distinguish between end-of-file and error, and callers must use feof and ferror to determine which occurred.

. , , . .. , , .







N35; || , &&



PVS-Studio: V547 Expression is always true. essl_sdio.c 209







esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms)
{
  ....
  // Set block sizes for functions 1 to given value (default value = 512).
  if (ctx->block_size > 0 || ctx->block_size <= 2048) {
    bs = ctx->block_size;
  } else {
    bs = 512;
  }
  ....
}
      
      





, , , , , . , , .







, . 0, 2048. - - 512.







:







if (ctx->block_size > 0 && ctx->block_size <= 2048) {
  bs = ctx->block_size;
} else {
  bs = 512;
}
      
      





N35 — N38;



PVS-Studio: V547 Expression 'depth <= 0' is always false. panic_handler.c 169







static void print_backtrace(const void *f, int core)
{
  XtExcFrame *frame = (XtExcFrame *) f;
  int depth = 100;                                          // <=
  //Initialize stk_frame with first frame of stack
  esp_backtrace_frame_t stk_frame =
    {.pc = frame->pc, .sp = frame->a1, .next_pc = frame->a0};
  panic_print_str("\r\nBacktrace:");
  print_backtrace_entry(esp_cpu_process_stack_pc(stk_frame.pc),
                        stk_frame.sp);

  //Check if first frame is valid
  bool corrupted =
    !(esp_stack_ptr_is_sane(stk_frame.sp) &&
      (esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc)) ||
       /* Ignore the first corrupted PC in case of InstrFetchProhibited */
       frame->exccause == EXCCAUSE_INSTR_PROHIBITED));

  //Account for stack frame that's already printed
  uint32_t i = ((depth <= 0) ? INT32_MAX : depth) - 1;      // <=
  ....
}
      
      





depth 100, . . - - ?







:







  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. event_groups.c 260
  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. tasks.c 1475
  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. tasks.c 1520


N39;



PVS-Studio: V614 Potentially uninitialized buffer 'k' used. Consider checking the second actual argument of the 'sae_derive_keys' function. sae.c 854







int sae_process_commit(struct sae_data *sae)
{
  u8 k[SAE_MAX_PRIME_LEN];
  if (sae->tmp == NULL ||
      (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
      (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
      sae_derive_keys(sae, k) < 0)
    return ESP_FAIL;
  return ESP_OK;
}
      
      





. , ec dh . k , sae_derive_keys .







N40;



PVS-Studio: V547 Expression 'bit_len == 32' is always false. spi_flash_ll.h 371







static inline void spi_flash_ll_set_usr_address(spi_dev_t *dev, uint32_t addr,
                                                int bit_len)
{
  // The blank region should be all ones
  if (bit_len >= 32) {
    dev->addr = addr;
    dev->slv_wr_status = UINT32_MAX;
  } else {
    uint32_t padding_ones = (bit_len == 32? 0 : UINT32_MAX >> bit_len);
    dev->addr = (addr << (32 - bit_len)) | padding_ones;
  }
}
      
      





, bit_len == 32 . , -- (>=), (>).







N41;



PVS-Studio: V519 The '* pad_num' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 46, 48. touch_sensor_hal.c 48







void touch_hal_get_wakeup_status(touch_pad_t *pad_num)
{
  uint32_t touch_mask = 0;
  touch_ll_read_trigger_status_mask(&touch_mask);
  if (touch_mask == 0) {
    *pad_num = -1;
  }
  *pad_num = (touch_pad_t)(__builtin_ffs(touch_mask) - 1);
}
      
      





else. , , , :







void touch_hal_get_wakeup_status(touch_pad_t *pad_num)
{
  uint32_t touch_mask = 0;
  touch_ll_read_trigger_status_mask(&touch_mask);
  if (touch_mask == 0) {
    *pad_num = -1;
  } else {
    *pad_num = (touch_pad_t)(__builtin_ffs(touch_mask) - 1);
  }
}
      
      







N42;



PVS-Studio: V557 Array overrun is possible. The value of 'frame->exccause' index could reach 16. gdbstub_xtensa.c 132







int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
{
  const char exccause_to_signal[] =
    {4, 31, 11, 11, 2, 6, 8, 0, 6, 7, 0, 0, 7, 7, 7, 7};
  if (frame->exccause > sizeof(exccause_to_signal)) {
    return 11;
  }
  return (int) exccause_to_signal[frame->exccause];
}
      
      





1 . , --:







if (frame->exccause >= sizeof(exccause_to_signal)) {
      
      





N43; :)



, :







  • V557 Array overrun is possible. The value of 'other_if' index could reach 3. mdns.c 2206
  • V557 Array overrun is possible. The '_mdns_announce_pcb' function processes value '[0..3]'. Inspect the first argument. Check lines: 1674, 2213. mdns.c 1674


, . :







typedef enum mdns_if_internal {
    MDNS_IF_STA = 0,
    MDNS_IF_AP = 1,
    MDNS_IF_ETH = 2,
    MDNS_IF_MAX
} mdns_if_t;
      
      





, MDNS_IF_MAX 3.







mdns_server_s. , interfaces 3 :







typedef struct mdns_server_s {
    struct {
        mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];
    } interfaces[MDNS_IF_MAX];
    const char * hostname;
    const char * instance;
    mdns_srv_item_t * services;
    SemaphoreHandle_t lock;
    QueueHandle_t action_queue;
    mdns_tx_packet_t * tx_queue_head;
    mdns_search_once_t * search_once;
    esp_timer_handle_t timer_handle;
} mdns_server_t;

mdns_server_t * _mdns_server = NULL;
      
      





. _mdns_get_other_if. , MDNS_IF_MAX. .. 3.







static mdns_if_t _mdns_get_other_if (mdns_if_t tcpip_if)
{
  if (tcpip_if == MDNS_IF_STA) {
    return MDNS_IF_ETH;
  } else if (tcpip_if == MDNS_IF_ETH) {
     return MDNS_IF_STA;
  }
  return MDNS_IF_MAX;
}
      
      





, , :







static void _mdns_dup_interface(mdns_if_t tcpip_if)
{
    uint8_t i;
    mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
    for (i=0; i<MDNS_IP_PROTOCOL_MAX; i++) {
        if (_mdns_server->interfaces[other_if].pcbs[i].pcb) {        // <=
            //stop this interface and mark as dup
            if (_mdns_server->interfaces[tcpip_if].pcbs[i].pcb) {
                _mdns_clear_pcb_tx_queue_head(tcpip_if, i);
                _mdns_pcb_deinit(tcpip_if, i);
            }
            _mdns_server->interfaces[tcpip_if].pcbs[i].state = PCB_DUP;
            _mdns_announce_pcb(other_if, i, NULL, 0, true);          // <=
        }
    }
}
      
      





, , _mdns_get_other_if . other_if . :







if (_mdns_server->interfaces[other_if].pcbs[i].pcb)
      
      





, other_if, - _mdns_announce_pcb:







_mdns_announce_pcb(other_if, i, NULL, 0, true);
      
      





:







static void _mdns_announce_pcb(mdns_if_t tcpip_if,
                               mdns_ip_protocol_t ip_protocol,
                               mdns_srv_item_t ** services,
                               size_t len, bool include_ip)
{
  mdns_pcb_t * _pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
  ....
}
      
      





3 , 3 . — .









N44 — N47;



PVS-Studio: V595 The 'hapd->wpa_auth' pointer was utilized before it was verified against nullptr. Check lines: 106, 113. esp_hostap.c 106







bool hostap_deinit(void *data)
{
  struct hostapd_data *hapd = (struct hostapd_data *)data;

  if (hapd == NULL) {
    return true;
  }

  if (hapd->wpa_auth->wpa_ie != NULL) {
    os_free(hapd->wpa_auth->wpa_ie);
  }

  if (hapd->wpa_auth->group != NULL) {
    os_free(hapd->wpa_auth->group);
  }

  if (hapd->wpa_auth != NULL) {
    os_free(hapd->wpa_auth);
  }
  ....
}
      
      





:







if (hapd->wpa_auth->group != NULL)
....
if (hapd->wpa_auth != NULL)
      
      





hapd->wpa_auth , . :







if (hapd->wpa_auth != NULL)
{
  ....
  if (hapd->wpa_auth->group != NULL)
  ....
}
      
      





:







  • V595 The 'hapd->conf' pointer was utilized before it was verified against nullptr. Check lines: 118, 125. esp_hostap.c 118
  • V595 The 'sm' pointer was utilized before it was verified against nullptr. Check lines: 1637, 1647. esp_wps.c 1637
  • V595 The 'sm' pointer was utilized before it was verified against nullptr. Check lines: 1693, 1703. esp_wps.c 1693


N48 — N64;



, , . .. :







dhcp_data = (struct dhcp *)malloc(sizeof(struct dhcp));
if (dhcp_data == NULL) {
  return ESP_ERR_NO_MEM;
}
      
      





.







PVS-Studio: V522 There might be dereferencing of a potential null pointer 'exp'. Check lines: 3470, 3469. argtable3.c 3470







TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags)
{
  TRex *exp = (TRex *)malloc(sizeof(TRex));
  exp->_eol = exp->_bol = NULL;
  exp->_p = pattern;
  ....
}
      
      





, . " , malloc".







, :







  • V522 There might be dereferencing of a potential null pointer 's_ledc_fade_rec[speed_mode][channel]'. Check lines: 668, 667. ledc.c 668
  • V522 There might be dereferencing of a potential null pointer 'environ'. Check lines: 108, 107. syscall_table.c 108
  • V522 There might be dereferencing of a potential null pointer 'it'. Check lines: 150, 149. partition.c 150
  • V522 There might be dereferencing of a potential null pointer 'eth'. Check lines: 167, 159. wpa_auth.c 167
  • V522 There might be dereferencing of a potential null pointer 'pt'. Check lines: 222, 219. crypto_mbedtls-ec.c 222
  • V522 There might be dereferencing of a potential null pointer 'attr'. Check lines: 88, 73. wps.c 88
  • V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 725, 724. coap_mbedtls.c 725
  • V575 The potential null pointer is passed into 'memset' function. Inspect the first argument. Check lines: 3504, 3503. argtable3.c 3504
  • V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 496, 495. mqtt_client.c 496
  • V575 The potential null pointer is passed into 'strcpy' function. Inspect the first argument. Check lines: 451, 450. transport_ws.c 451
  • V769 The 'buffer' pointer in the 'buffer + n' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 186, 181. cbortojson.c 186
  • V769 The 'buffer' pointer in the 'buffer + len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 212, 207. cbortojson.c 212
  • V769 The 'out' pointer in the 'out ++' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 233, 207. cbortojson.c 233
  • V769 The 'parser->m_bufferPtr' pointer in the expression equals nullptr. The resulting value of arithmetic operations on this pointer is senseless and it should not be used. xmlparse.c 2090
  • V769 The 'signature' pointer in the 'signature + curve->prime_len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 4112, 4110. dpp.c 4112
  • V769 The 'key' pointer in the 'key + 16' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 634, 628. eap_mschapv2.c 634


N65, N66; ( )



, , . , realloc.







PVS-Studio: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'exp->_nodes' is lost. Consider assigning realloc() to a temporary pointer. argtable3.c 3008







static int trex_newnode(TRex *exp, TRexNodeType type)
{
  TRexNode n;
  int newid;
  n.type = type;
  n.next = n.right = n.left = -1;
  if(type == OP_EXPR)
    n.right = exp->_nsubexpr++;
  if(exp->_nallocated < (exp->_nsize + 1)) {
    exp->_nallocated *= 2;
    exp->_nodes = (TRexNode *)realloc(exp->_nodes,
                                      exp->_nallocated * sizeof(TRexNode));
  }
  exp->_nodes[exp->_nsize++] = n; // NOLINT(clang-analyzer-unix.Malloc)
  newid = exp->_nsize - 1;
  return (int)newid;
}
      
      





-, realloc NULL, exp->_nodes. .







-, realloc NULL, . :







exp->_nodes[exp->_nsize++] = n;
      
      





exp->_nsize++ , , - , , , . , .







: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'm_context->pki_sni_entry_list' is lost. Consider assigning realloc() to a temporary pointer. coap_mbedtls.c 737









N67;



PVS-Studio: V547 Expression 'ret != 0' is always false. sdio_slave.c 394







esp_err_t sdio_slave_start(void)
{
  ....
  critical_exit_recv();
  ret = ESP_OK;
  if (ret != ESP_OK) return ret;

  sdio_slave_hal_set_ioready(context.hal, true);
  return ESP_OK;
}
      
      





, :







esp_err_t sdio_slave_start(void)
{
  ....
  critical_exit_recv();
  sdio_slave_hal_set_ioready(context.hal, true);
  return ESP_OK;
}
      
      





, . , , . , , , . , . — .







N68;



PVS-Studio: V547 Expression 'err != 0' is always false. sdio_slave_hal.c 96







static esp_err_t sdio_ringbuf_send(....)
{
  uint8_t* get_ptr = ....;
  esp_err_t err = ESP_OK;
  if (copy_callback) {
    (*copy_callback)(get_ptr, arg);
  }
  if (err != ESP_OK) return err;

  buf->write_ptr = get_ptr;
  return ESP_OK;
}
      
      





-, , . err .







N69;



PVS-Studio: V614 Potentially uninitialized buffer 'seq' used. Consider checking the first actual argument of the 'strlen' function. linenoise.c 435







void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
    char seq[64];
    if (hintsCallback && plen+l->len < l->cols) {
        int color = -1, bold = 0;
        char *hint = hintsCallback(l->buf,&color,&bold);
        if (hint) {
            int hintlen = strlen(hint);
            int hintmaxlen = l->cols-(plen+l->len);
            if (hintlen > hintmaxlen) hintlen = hintmaxlen;
            if (bold == 1 && color == -1) color = 37;
            if (color != -1 || bold != 0)
                snprintf(seq,64,"\033[%d;%d;49m",bold,color);
            abAppend(ab,seq,strlen(seq));                       // <=
            abAppend(ab,hint,hintlen);
            if (color != -1 || bold != 0)
                abAppend(ab,"\033[0m",4);
            /* Call the function to free the hint returned. */
            if (freeHintsCallback) freeHintsCallback(hint);
        }
    }
}
      
      





seq , ! :







if (color != -1 || bold != 0)
  snprintf(seq,64,"\033[%d;%d;49m",bold,color);
      
      





, , . ab.







, :







if (color != -1 || bold != 0)
{
  snprintf(seq,64,"\033[%d;%d;49m",bold,color);
  abAppend(ab,seq,strlen(seq));
}
      
      





N70;



PVS-Studio: V547 Expression is always false. tasks.c 896







#ifndef portPRIVILEGE_BIT
  #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )
#endif

static void prvInitialiseNewTask(...., UBaseType_t uxPriority, ....)
{
  StackType_t *pxTopOfStack;
  UBaseType_t x;

  #if (portNUM_PROCESSORS < 2)
  xCoreID = 0;
  #endif

  #if( portUSING_MPU_WRAPPERS == 1 )
    /* Should the task be created in privileged mode? */
    BaseType_t xRunPrivileged;
    if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
    {
      xRunPrivileged = pdTRUE;
    }
    else
    {
      xRunPrivileged = pdFALSE;
    }
  ....
}
      
      





portPRIVILEGE_BIT 0. :







if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
      
      





N71,



PVS-Studio: V773 The function was exited without releasing the 'sm' pointer. A memory leak is possible. esp_wpa2.c 753







static int eap_peer_sm_init(void)
{
  int ret = 0;
  struct eap_sm *sm;
  ....
  sm = (struct eap_sm *)os_zalloc(sizeof(*sm));
  if (sm == NULL) {
    return ESP_ERR_NO_MEM;
  }

  s_wpa2_data_lock = xSemaphoreCreateRecursiveMutex();
  if (!s_wpa2_data_lock) {
    wpa_printf(MSG_ERROR, ".......");  // NOLINT(clang-analyzer-unix.Malloc)
    return ESP_ERR_NO_MEM;             // <=
  }
  ....
}
      
      





xSemaphoreCreateRecursiveMutex , eap_peer_sm_init . , os_free :







  s_wpa2_data_lock = xSemaphoreCreateRecursiveMutex();
  if (!s_wpa2_data_lock) {
    wpa_printf(MSG_ERROR, ".......");
    os_free(sm);
    return ESP_ERR_NO_MEM;
  }
      
      





, Clang . - :







// NOLINT(clang-analyzer-unix.Malloc)
      
      





. . , , , .









. , . . , :).







PVS-Studio . :







  1. , ;
  2. , , ;
  3. ;
  4. , . , — . - .


PVS-Studio. , , . , , PVS-Studio .







, : Andrey Karpov. Espressif IoT Development Framework: 71 Shots in the Foot.








All Articles