Skip to content

Signed overflow in cbor_value_get_int64() / cbor_value_get_int() for out-of-range negative integers #347

Open

Description

@Tajwarbot

cbor_value_get_int64() and cbor_value_get_int() in src/cbor.h do

*result = -*result - 1;

for negative integers. If the encoded magnitude is 2^63 (or 2^31 for the int version), *result is already INT64_MIN / INT_MIN at that point, so the negation overflows. That's UB, and it's reachable from untrusted input through the public API. The only precondition is cbor_value_is_integer(), which these inputs satisfy.

Inputs:

  • 3b 80 00 00 00 00 00 00 00, i.e. -2^63 - 1, hits cbor_value_get_int64()
  • 3a 80 00 00 00, i.e. -2^31 - 1, hits cbor_value_get_int()

With clang -fsanitize=undefined on current main (da6421f):

src/cbor.h:437:19: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long')
src/cbor.h:446:19: runtime error: negation of -2147483648 cannot be represented in type 'int'

Minimal repro:

#include <stdio.h>
#include <stdint.h>
#include "cbor.h"

int main(void)
{
    static const uint8_t data[] = { 0x3b, 0x80, 0, 0, 0, 0, 0, 0, 0 };
    CborParser parser;
    CborValue it;
    int64_t v;
    if (cbor_parser_init(data, sizeof(data), 0, &parser, &it) || !cbor_value_is_integer(&it))
        return 1;
    cbor_value_get_int64(&it, &v);
    printf("%lld\n", (long long)v);
    return 0;
}

I know the docs say to use the _checked variants if the value might not fit, and those are fine. But the unchecked ones are documented as returning a truncated value in that case, not as UB, and in practice a sanitizer build aborts. An optimising compiler is also free to assume the overflow can't happen.

I ran into this while writing CBMC harnesses and fuzzing tinycbor. Happy to open a PR with a fix (~*result instead of -*result - 1, same value, no overflow) and a test if that sounds good.

Activity

  1. thiagomacieira commented on Oct 26, 2026

    @thiagomacieira
    Member

    Hello @Tajwarbot

    Thank you for your report. The documentation, as you noted, is very clear:

     * Note that this function does not do range-checking: integral values that do
     * not fit in a variable of type \c int64_t are silently truncated to fit. Use
     * cbor_value_get_int64_checked() that is not acceptable.
    

    I think I can change the implementation to use unsigned arithmetic at this point, which will not incur UB.

  2. thiagomacieira commented on Oct 26, 2026

    @thiagomacieira
    Member

    Yes, that worked. Fix in #349

  3. added a commit that references this issue on Oct 27, 2026
    cf7f4d0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions