manish ma
manish ma

Reputation: 2038

Read string buffer using sscanf

I have a TCP server transmitting JSON formatted strings over a standard TCP socket. The message consists of :| length of JSON object | JSON object |

On the client I need to take this message and parse it. So first I extract the length using sscanf, move the pointer sscanf'ed bytes, and extract the JSON message. Currently I'm stuck at the sscanf part, and get an "address out of bounds" error while trying to extract integer value from the string buffer.

Why does this happen? please help.

static void * oom_collect_xcvr_data_thread(void *arg)
{
    int offset = 0;
    int recv_len, data_size;
    char *recv_buf = NULL;
    json_t *json_obj = NULL;
    json_error_t j_error;

    recv_buf = calloc(OOM_BUF_SIZE, 1);
    CASSERT(recv_buf != NULL);

    while(1)
    {
        if((recv_len = recv(sock_fd, recv_buf, OOM_BUF_SIZE, 0)) <= 0)
        {
            printf("server connect: tcp receive error %s", strerror(errno));
            free(recv_buf);
            CASSERT(0);
        }

        while(offset < recv_len)
        {
            offset += sscanf(recv_buf + offset, "%d \n", &data_size);
            if ((json_obj = json_loadb(recv_buf + offset, data_size, 0, &j_error)) == NULL)
            {
                    printf("line: %d, column: %d, position: %d, source: %s, Error: %s"
                            ,j_error.line, j_error.column, j_error.position, j_error.source, j_error.text); 
            }

            offset += data_size;
            copy_xcvr_info(json_obj);
        }
        CASSERT(offset == recv_len);
        offset = 0;
    }
}

String data I'm trying to parse:

1905
{  
   "static":{  
      "RX_POWER_HIGH_ALARM":2.5,
      "LENGTH_SMF":10000,
      "update_timestamp":251739.961,
      "ENCODING":6,
      "ENHANCED_OPTIONS":240,
      "LENGTH_SMF_KM":10000,
      "TX_POWER_LOW_ALARM":-8.0,
      "CONNECTOR":7,
      "DIAGNOSTIC_MONITORING_TYPE":104,
      "TRANSCEIVER_EXT":0,
      "VENDOR_PN":"FTLX1471D3BCL",
      "RX_POWER_LOW_WARN":-18.01,
      "WAVELENGTH":1310,
      "OPTIONS":"001a",
      "LENGTH_OM4_OR_CU":0,
      "TEMP_HIGH_WARN":73.0,
      "TEMP_LOW_ALARM":-13.0,
      "BR_NOMINAL":10300,
      "VOLTAGE_LOW_ALARM":2.9,
      "BIAS_HIGH_ALARM":85.0,
      "LENGTH_62_5UM":0,
      "VOLTAGE_LOW_WARN":3.0,
      "RATE_IDENTIFIER":0,
      "BIAS_LOW_ALARM":15.0,
      "VENDOR_OUI":"009065",
      "BIAS_LOW_WARN":20.0,
      "CABLE_SPEC":"0000",
      "TX_POWER_HIGH_WARN":1.0,
      "EXT_IDENTIFIER":4,
      "update_count":1,
      "VENDOR_SN":"UK70M7N",
      "VOLTAGE_HIGH_ALARM":3.7,
      "TX_POWER_HIGH_ALARM":2.0,
      "IDENTIFIER":3,
      "LENGTH_OM3":0,
      "BR_MIN":10300,
      "TEMP_HIGH_ALARM":78.0,
      "SFF_8472_COMPLIANCE":3,
      "RX_POWER_LOW_ALARM":-20.0,
      "VOLTAGE_HIGH_WARN":3.6,
      "BIAS_HIGH_WARN":80.0,
      "TRANSCEIVER":"2000000000000000",
      "LENGTH_50UM":0,
      "TX_POWER_LOW_WARN":-7.0,
      "VENDOR_REV":"A",
      "DATE_CODE":"110212",
      "RX_POWER_HIGH_WARN":2.0,
      "VENDOR_NAME":"FINISAR CORP.",
      "BR_MAX":10300,
      "TEMP_LOW_WARN":-8.0
   },
   "port_name":"port18",
   "port_type":"SFP",
   "dynamic":{  
      "L_TX_POWER_WARN":0,
      "update_timestamp":253134.25,
      "L_TX_POWER_ALARM":0,
      "TX_POWER":0.68,
      "L_RX_POWER_ALARM":0,
      "DATA_READY_BAR_STATE":0,
      "RS_1_STATE":0,
      "TX_DISABLE_STATE":0,
      "L_TEMP_WARN":0,
      "L_VCC_ALARM":0,
      "L_ALARM_WARN":"000000000000",
      "SOFT_RATE_SELECT":0,
      "TX_FAULT_STATE":0,
      "L_TEMP_ALARM":0,
      "TX_POWER_DBM":-1.68,
      "VCC":3.41,
      "TEMPERATURE":32.99,
      "TX_BIAS":37.19,
      "STATUS_CONTROL":0,
      "L_BIAS_ALARM":0,
      "RX_LOS_STATE":0,
      "OPT_LASER_TEMP":0.0,
      "L_BIAS_WARN":0,
      "RX_POWER_DBM":-1.28,
      "SOFT_TX_DISABLE_SELECT":0,
      "L_RX_POWER_WARN":0,
      "OPT_TEC":0.0,
      "RX_POWER":0.75,
      "L_VCC_WARN":0,
      "RATE_SELECT_STATE":0
   }
}   

gdb error message:

Program received signal SIGSEGV, Segmentation fault.
rawmemchr () at ../sysdeps/i386/rawmemchr.S:70
70      ../sysdeps/i386/rawmemchr.S: No such file or directory.
(gdb) bt
#0  rawmemchr () at ../sysdeps/i386/rawmemchr.S:70
#1  0xf63de127 in _IO_str_init_static_internal (sf=0xee807838, ptr=0x969d046 <Address 0x969d046 out of bounds>, size=157929542, pstart=0x0) at strops.c:45
#2  0xf63d1c43 in _IO_vsscanf (string=0x969d046 <Address 0x969d046 out of bounds>, format=0x80d5fb7 "%d \n", args=0xee807908 "\260\202\200\356") at iovsscanf.c:44
#3  0xf63bf59b in __sscanf (s=0x969d046 <Address 0x969d046 out of bounds>, format=0x80d5fb7 "%d \n") at sscanf.c:34
#4  0x08091013 in oom_collect_xcvr_data_thread (arg=0x0) at /home/sfreeman/wspace/swapp/src/interface/agent/ia_l2.c:172
#5  0xf730f954 in start_thread (arg=0xee808b70) at pthread_create.c:304
#6  0xf644295e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:130

Upvotes: 0

Views: 632

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136415

sscanf causes SIGSEGV when the receive buffer is full and doesn't end with 0. You need to ensure the received data is terminated with 0, e.g.:

recv_len = recv(sock_fd, recv_buf, OOM_BUF_SIZE - 1, 0);
if(recv_len > 0) {
    recv_buf[recv_len] = 0;
}
else {
    // Handle disconnect or error.
}

I also spotted the following errors:

  • The code cannot handle partial messages.
  • sscanf returns the number of items assigned, not the number of consumed bytes, like the code expects.
  • No checking of sscanf return value for errors.
  • No checking whether the number has been received in full. sscanf format " \n" matches 0-length string, contrary to what the code expects.

Reading sscanf man page in its entirety is required to be able to use it correctly.

Upvotes: 2

Related Questions