[CLIENT-4845] Fix user dictionary returning only the first entry of read_info and write_info array of integers#1074
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #1074 +/- ##
==========================================
+ Coverage 84.44% 84.46% +0.02%
==========================================
Files 99 99
Lines 14062 14088 +26
==========================================
+ Hits 11874 11899 +25
- Misses 2188 2189 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
… TODO this may cause macos builds to fail
…nullable_array_of_uint32_to_py_list
f5e6601 to
a2cf9e3
Compare
…y apply -Werror to linux builds
…nger-builds-python-client-with-Werror' into CLIENT-4845-fix-user-dictionary-only-returning-integers-instead-of-list-of-ints
00169e6 to
9a17540
Compare
…believe using void** parameter is dangerous because to accept an array of ints, we would need to cast void* to int which can be dangerous where int is only 32-bit like on windows
…erating on each string. The array isn't an array of string pointers.
4132f6a to
4e7fcdb
Compare
…ictionary-only-returning-integers-instead-of-list-of-ints
…s.c, so make it static inline
… user dictionary. (Although this wouldn't matter if conns_in_use is between -5 and 256)
| char format_str[2]; | ||
| sprintf(format_str, "%c", format_specifier); | ||
| switch (format_specifier) { | ||
| case 'k': { |
There was a problem hiding this comment.
AI picked this up, looks like it is worth using I here.
Py_BuildValue format 'k' doesn't match the uint32_t argument
In convert_nullable_array_to_py_optional_list:
case 'k': {
uint32_t element = ((uint32_t *)array)[i];
py_element = Py_BuildValue(format_str, element); // format_str == "k"
break;
}
read_info and write_info are uint32_t* (confirmed in as_admin.h: uint32_t* read_info;, uint32_t* write_info;), and uint32_t is unsigned
int on all target platforms. But the 'k' format code tells Py_BuildValue to read an unsigned long via va_arg.
This is a varargs type mismatch (undefined behavior):
- Windows (LLP64): unsigned long is 32-bit → happens to match, fine.
- Linux/macOS x86-64 (LP64): unsigned long is 64-bit. The 32-bit unsigned int is passed in a register that the compiler typically
zero-extends, so it usually works by luck — but it's relying on UB. - aarch64 (LP64): unsigned long is also 64-bit. The AAPCS64 ABI does not guarantee the upper 32 bits of a narrow variadic slot are
zeroed, so va_arg(ap, unsigned long) can read garbage in the high word → wrong read_info/write_info integers. They build and ship aarch64
wheels, so this is a genuine cross-platform correctness risk, not purely theoretical.
Fix: use the 'I' (unsigned int) format code for uint32_t:
case 'I': {
uint32_t element = ((uint32_t *)array)[i];
py_element = Py_BuildValue(format_str, element);
break;
}
(and pass 'I' instead of 'k' from as_user_info_to_pyobject). Alternatively keep 'k' but cast the argument: Py_BuildValue("k", (unsigned
long)element). The 'I' change is cleaner. Note their valgrind/CI run on x86-64 will not catch this because of the incidental
zero-extension.
There was a problem hiding this comment.
I don't think it's safe to use I (unsigned int) as the format specifier since it's only guaranteed to be at least 2 bytes large. In theory if unsigned int was 2 bytes large, I think passing in an unsigned 32-bit integer value to I would cause the value to be truncated or cause undefined behavior.
unsigned long is always at least 32 bits long, so I think the unsigned long cast is not needed; unsigned long can always accomodate a uint32_t value.
There was a problem hiding this comment.
Agreed on the I point. I think the problem is when k causes Py_BuildValue to read 64 bits where element is only a 32 bit unsigned int that isn't promoted because Py_BuildValue uses variadic arguments. I think Py_BuildValue("k", (unsigned long)element); would make this safe. Let me know what you think.
There was a problem hiding this comment.
Ok I'll make that change
…ictionary-only-returning-integers-instead-of-list-of-ints
|
Noticed one failing test case related to bin projection on macos x86. Wasn't able to get any meaningful server logs: These changes aren't related to bin projection, but I'll just rerun the bin projection tests on macos x86 a few times to see how reproducible it is. If I can't reproduce the error then I'll merge this EDIT - Reran the bin projection tests on macos x86 two times, and they all pass. https://github.com/aerospike/aerospike-client-python/actions/runs/26918869948 TODO - need to log a Jira ticket w/ workflow run |
https://aerospike-python-client.readthedocs.io/en/latest/client.html#user-dictionary
Tests for admin_query_user_info() and admin_query_users_info() were not checking the user dictionary correctly. Now these tests match the documentation
Extra changes
Manual testing