contrib/schemagen/weaseljson_schemagen.py tracks object field presence with a 32-bit bitmask. The generated builder uses uint32_t seen (template line 797) and shifts such as 1u << p.field (line 832) and 1u << idx (line 884). The required mask is built by _bitmask (line 665) and emitted by _reqmask (line 672).
For schemas with more than 32 properties this breaks in two ways:
The generated requiredMask literal overflows uint32_t. For 40 required fields it emits 0xffffffffffu, which is truncated to 0xffffffffu at compile time (GCC warns: "conversion from ... changes value").
Shifts by field indices >= 32 are undefined behavior in C++. On typical compilers the shift wraps modulo 32, so fields p32..p39 alias bits 0..7. This makes p32 appear as a duplicate of p0, and a document that is genuinely missing p32 (or any index >=32) can be accepted because the truncated mask no longer contains that bit.
Reproducer: generate from a schema with 40 required string properties and feed a JSON object that supplies all of them except p32. The builder accepts it even though p32 is required. Feeding a document with both p0 and p32 causes a false duplicate-key rejection.
The generator should either reject schemas with more than 32 properties or, preferably, use a bitset / uint64_t / vector-backed tracker sized to the actual field count.
`contrib/schemagen/weaseljson_schemagen.py` tracks object field presence with a 32-bit bitmask. The generated builder uses `uint32_t seen` (template line 797) and shifts such as `1u << p.field` (line 832) and `1u << idx` (line 884). The required mask is built by `_bitmask` (line 665) and emitted by `_reqmask` (line 672).
For schemas with more than 32 properties this breaks in two ways:
1. The generated `requiredMask` literal overflows `uint32_t`. For 40 required fields it emits `0xffffffffffu`, which is truncated to `0xffffffffu` at compile time (GCC warns: "conversion from ... changes value").
2. Shifts by field indices `>= 32` are undefined behavior in C++. On typical compilers the shift wraps modulo 32, so fields `p32`..`p39` alias bits `0`..`7`. This makes `p32` appear as a duplicate of `p0`, and a document that is genuinely missing `p32` (or any index >=32) can be accepted because the truncated mask no longer contains that bit.
Reproducer: generate from a schema with 40 required string properties and feed a JSON object that supplies all of them except `p32`. The builder accepts it even though `p32` is required. Feeding a document with both `p0` and `p32` causes a false duplicate-key rejection.
The generator should either reject schemas with more than 32 properties or, preferably, use a bitset / `uint64_t` / vector-backed tracker sized to the actual field count.
weaselbot
was assigned by andrew2026-06-18 14:05:34 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
contrib/schemagen/weaseljson_schemagen.pytracks object field presence with a 32-bit bitmask. The generated builder usesuint32_t seen(template line 797) and shifts such as1u << p.field(line 832) and1u << idx(line 884). The required mask is built by_bitmask(line 665) and emitted by_reqmask(line 672).For schemas with more than 32 properties this breaks in two ways:
requiredMaskliteral overflowsuint32_t. For 40 required fields it emits0xffffffffffu, which is truncated to0xffffffffuat compile time (GCC warns: "conversion from ... changes value").>= 32are undefined behavior in C++. On typical compilers the shift wraps modulo 32, so fieldsp32..p39alias bits0..7. This makesp32appear as a duplicate ofp0, and a document that is genuinely missingp32(or any index >=32) can be accepted because the truncated mask no longer contains that bit.Reproducer: generate from a schema with 40 required string properties and feed a JSON object that supplies all of them except
p32. The builder accepts it even thoughp32is required. Feeding a document with bothp0andp32causes a false duplicate-key rejection.The generator should either reject schemas with more than 32 properties or, preferably, use a bitset /
uint64_t/ vector-backed tracker sized to the actual field count.