CVE-2020-12887 (C, published 2020-06-18T19:15Z)
Description: value: Memory leaks were discovered in the CoAP library in Arm Mbed OS 5.15.3 when using the Arm mbed-coap library 5.1.5. The CoAP parser is responsible for parsing received CoAP packets. The function sn_coap_parser_options_parse() parses the CoAP option number field of all options present in the input packet. Each option number is calculated as a sum of the previous option number and a delta of the current option. The delta and the previous option number are expressed as unsigned 16-bit integers. Due to lack of overflow detection, it is possible to craft a packet that wraps the option number around and results in the same option number being processed again in a single packet. Certain options allocate memory by calling a memory allocation function. In the cases of COAP_OPTION_URI_QUERY, COAP_OPTION_URI_PATH, COAP_OPTION_LOCATION_QUERY, and COAP_OPTION_ETAG, there is no check on whether memory has already been allocated, which in conjunction with the option number integer overflow may lead to multiple assignments of allocated memory to a single pointer. This has been demonstrated to lead to memory leak by buffer orphaning. As a result, the memory is never freed.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-401 \u2014 Missing Release of Memory after Effective Lifetime
The product does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory.
Repository: https://github.com/mjurczak/mbed-coap
Files changed:
source/sn_coap_parser.c
Commit message: Implemented measures to prevent memory leaks in sn_coap_parser_options_parse().

Added a helper uint16_t addition function with overflow detection. The function is used when calculating the extended length and option delta. The overlow detection is needed to avoid wrap-around of option number or length.
Additional checks in options using sn_coap_parser_options_parse_multiple_options() have been implemented to avoid overwriting of pointers pointing to previously allocated memory.


Vulnerable code:
                option_number += option_ext;
                *message_left = sn_coap_parser_move_packet_ptr(packet_data_pptr,
                                                               packet_data_start_ptr,
                                                               packet_len,
                                                               1);
            option_number += 269;
            *message_left = sn_coap_parser_move_packet_ptr(packet_data_pptr,
                                                           packet_data_start_ptr,
                                                           packet_len,
                                                           2);
        option_number += previous_option_number;

Fixed code:
/**
 * \brief Add u16 integers with overflow detection
 *
 * \param a            first term of addition
 * \param b            second term of addion
 * \param result       pointer to the result variable
 *
 * \return Return 0 if there was no overflow, -1 otherwise
 */
static int8_t sn_coap_parser_add_u16_limit(uint16_t a, uint16_t b, uint16_t *result)
{
    uint16_t c;

    c = a + b;
    if (c < a || c < b)
    {
        return -1;
    }

    *result = c;

    return 0;
}
            if(sn_coap_parser_add_u16_limit(option_number, option_ext, &option_number) != 0)
            {
                return -1;
            }

            *message_left = sn_coap_parser_move_packet_ptr(packet_data_pptr,
                                                            packet_data_start_ptr,
                                                            packet_len,
                                                            1);
                if(sn_coap_parser_add_u16_limit(option_number, 269, &option_number) != 0)
                {
                    return -1;
                }

                *message_left = sn_coap_parser_move_packet_ptr(packet_data_pptr,
                                                            packet_data_start_ptr,
                                                            packet_len,
                                                            2);
        if(sn_coap_parser_add_u16_limit(option_number, previous_option_number, &option_number) != 0)
        {
            return -1;
        }
                if (dst_coap_msg_ptr->options_list_ptr->etag_ptr)
                {
                    tr_error("sn_coap_parser_options_parse - COAP_OPTION_ETAG exists!");
                    return -1;
                }
                if (dst_coap_msg_ptr->options_list_ptr->location_query_ptr)
                {
                    tr_error("sn_coap_parser_options_parse - COAP_OPTION_LOCATION_QUERY exists!");
                    return -1;
                }
                if (dst_coap_msg_ptr->uri_path_ptr)
                {
                    tr_error("sn_coap_parser_options_parse - COAP_OPTION_URI_PATH exists!");
                    return -1;
                }