Skip to content

[Security] NULL pointer dereference in cbor_serialized_size() and cbor_serialize_tag() via cbor_move(cbor_tag_item()) #416

@Benjamin608608

Description

@Benjamin608608

Summary

NULL pointer dereference in cbor_serialized_size() and cbor_serialize_tag() via cbor_move(cbor_tag_item()) when a tag item has no tagged item set, introduced by incomplete fix in commit e87cc36.

Vulnerability Details

  • Type: NULL Pointer Dereference (CWE-476)
  • Severity: Medium
  • Location: src/cbor/serialization.c, lines 146 and 371
  • Functions: cbor_serialized_size, cbor_serialize_tag
  • Affected versions: All versions since commit e87cc36 (which made cbor_tag_item() return NULL when no item is set)

Description

Commit e87cc36 correctly changed cbor_tag_item() to return NULL when no tagged item is set. However, several call sites were not updated to handle the new NULL return value.

In cbor_serialized_size (line 146):

case CBOR_TYPE_TAG: {
    return _cbor_safe_signaling_add(
        _cbor_encoded_header_size(cbor_tag_value(item)),
        cbor_serialized_size(cbor_move(cbor_tag_item(item))));
}

In cbor_serialize_tag (line 371):

size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)),
                                     buffer + written, buffer_size - written);

cbor_move() unconditionally dereferences its argument (item->refcount--), so passing NULL causes a crash.

The same pattern also exists in cbor.c:

  • Line 287: cbor_copy() for tags
  • Line 424: cbor_copy_definite() for tags
  • Line 550: _cbor_nested_describe() for tags

Root Cause

Incomplete fix: cbor_tag_item() was updated to return NULL, but callers using cbor_move(cbor_tag_item(...)) were not updated to check for NULL first.

Impact

  • Crash (segfault) when serializing, copying, or describing a tag item that has no tagged item set
  • While cbor_load() always produces tags with items set, users constructing CBOR items via the API can trigger this
  • Libraries building on libcbor that construct tags programmatically are at risk

Proof of Concept

#include "cbor.h"

int main() {
    cbor_item_t* tag = cbor_new_tag(42);
    // Do NOT call cbor_tag_set_item() -- leave item unset
    
    size_t size = cbor_serialized_size(tag);  // CRASH: NULL deref
    // Or:
    // size_t buf_size;
    // unsigned char* buf;
    // cbor_serialize_alloc(tag, &buf, &buf_size);  // Also crashes
    
    cbor_decref(&tag);
    return 0;
}

Suggested Fix

Add NULL checks before cbor_move() at all affected call sites:

// serialization.c line 146
case CBOR_TYPE_TAG: {
    cbor_item_t* tagged = cbor_tag_item(item);
    if (tagged == NULL) return 0;
    return _cbor_safe_signaling_add(
        _cbor_encoded_header_size(cbor_tag_value(item)),
        cbor_serialized_size(cbor_move(tagged)));
}

// serialization.c line 371
cbor_item_t* tagged = cbor_tag_item(item);
if (tagged == NULL) return written;
size_t item_written = cbor_serialize(cbor_move(tagged),
                                     buffer + written, buffer_size - written);

Apply similar fixes in cbor.c at lines 287, 424, and 550.

Affected Version

Tested on latest master commit. Affects all versions since commit e87cc36.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions