Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid union access for get_ref/get_ptr with unsigned integer #4475

Closed
2 tasks done
cschreib-ibex opened this issue Oct 11, 2024 · 2 comments · Fixed by #4525
Closed
2 tasks done

Invalid union access for get_ref/get_ptr with unsigned integer #4475

cschreib-ibex opened this issue Oct 11, 2024 · 2 comments · Fixed by #4525
Assignees
Labels
confirmed kind: bug solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@cschreib-ibex
Copy link

cschreib-ibex commented Oct 11, 2024

Description

When a JSON value is stored as unsigned integer, it is possible to call get_ref<number_integer_t>() or get_ptr<number_integer_t>() without error, which accesses the signed integer member of the internal union instead of the unsigned member. This is undefined behaviour in C++ standard and should not be allowed (especially since trying access with other more clearly incompatible types, such as strings, is already checked and reported as an error).

I note that a corresponding unit test exists but has been commented out:

//CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
// "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);

The root of the problem is that is_number_integer() returns true for both signed and unsigned integer storage, yet is used to guard reference/pointer access using signed integers:

json/include/nlohmann/json.hpp

Lines 1464 to 1467 in 6325839

number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
{
return is_number_integer() ? &m_data.m_value.number_integer : nullptr;
}

I also note the docs say that "Writing data to the referee of the result yields an undefined state.", which isn't very clear. Does this mean we are not supposed to write a value to the object pointed to by the reference/pointer? The earlier wording (changed in 4e52277#diff-b56a00981d8f3b87e3ce49a7eb27d36f4586d9c54c3fb628a88cfc000aa5fed4L2632) was "The pointer becomes invalid if the underlying JSON object changes.", which made more sense.

Reproduction steps

See code example below.

Expected vs. actual results

Expected: type error exception.
Actual: no error, unsigned data is access as signed data (undefined behaviour).

Minimal code example

#include <nlohmann/json.hpp>
#include <iostream>

int main() {
    using namespace nlohmann;

    json j = json::number_unsigned_t{1u};
    assert(j.is_number_unsigned());

    j.get_ref<json::number_integer_t&>() = -1;
    assert(j.is_number_unsigned());
    
    std::cout << j.get<json::number_unsigned_t>() << std::endl;
    return 0;
}

Error messages

None.

Compiler and operating system

Linux x64, gcc 11

Library version

3.11.3

Validation

@nlohmann
Copy link
Owner

I can confirm the issue. Indeed, using is_number_integer() is wrong there. I'll check and also have a look at the wording of the warning.

@nlohmann nlohmann self-assigned this Nov 30, 2024
@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Nov 30, 2024
@nlohmann nlohmann added this to the Release 3.11.4 milestone Dec 6, 2024
@cschreib-ibex
Copy link
Author

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed kind: bug solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants