Conversation
get_data()/set_data() copied sizeof(BitField) bytes unconditionally. For bit-aligned images whose last pixel occupies fewer bits than a full BitField (e.g. bit_aligned_image3_type<1,2,1> = 4 bits/px on a 16-bit field), accessing the final pixel read or wrote past the end of the image buffer (heap-buffer-overflow, boostorg#808). Copy only ceil((first_bit + NumBits) / 8) bytes -- the bytes that actually contain the referenced channel's bits. Untouched bits keep their storage contents since the read-modify-write in set_unsafe() writes back exactly the byte range it read.
Contributor
|
Hi, thanks for running your AI agent on this issue. Could you please tell me what motivated you to create this PR and eventually which of your project uses GIL? |
Author
|
Hi @sdebionne, happy to clarify. No project of mine uses GIL directly — I audit memory safety in open-source image and parsing libraries, and your report in #808 was a clean, well-scoped bug with an MWE, so I reproduced it under ASan and sent a bounded-copy fix upstream. I hope it helps; happy to reshape the approach or add a regression test if you'd prefer a different structure. |
sdebionne
reviewed
Oct 1, 2026
|
|
||
| auto operator=(mutable_reference const& ref) const -> packed_channel_reference const& { set_from_reference(ref.get_data()); return *this; } | ||
| auto operator=(const_reference const& ref) const -> packed_channel_reference const& { set_from_reference(ref.get_data()); return *this; } | ||
| auto operator=(mutable_reference const& ref) const -> packed_channel_reference const& { set_from_reference(ref.get_data((FirstBit + NumBits + 7) / 8)); return *this; } |
Contributor
There was a problem hiding this comment.
Could you factorize the (FirstBit + NumBits + 7) / 8) as a static constexpr and give it a meaningful name ?
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue
Fixes #808 —
get_data()/set_data()inpacked_channel_reference_basecopiedsizeof(BitField)bytes unconditionally. For bit-aligned images whose last pixel occupies fewer bits than a fullBitField(e.g.bit_aligned_image3_type<1,2,1>= 4 bits/px on a 16-bit field), accessing the final pixel read or wrote past the end of the image buffer. Reproduced locally as a heap-buffer-overflow under ASan on the issue's MWE, and it is not limited to tiny images: any bit-aligned image whose total bit count is not a multiple of theBitFieldwidth truncates its last field (an 8×2 rgb121 image overruns its 8-byte buffer the same way).Root cause
A channel reference only ever needs the bytes that contain its bit range
[first_bit, first_bit + NumBits)relative todata_ptr, but both the static (packed_channel_reference) and dynamic (packed_dynamic_channel_reference) paths always copied the fullBitField. The copy is byte-wise (static_copy_bytes), so the overrun hits both reads (get) and writes (set_unsafe,set_from_reference).Fix
Add bounded
get_data(byte_count)/set_data(val, byte_count)overloads topacked_channel_reference_baseand call them withceil((first_bit + NumBits) / 8)— compile-time forpacked_channel_reference, runtime for the dynamic references. The clamp tosizeof(bitfield_t)keeps degenerate offsets in bounds. Because the copy is byte-address-ordered into/out of the bitfield object, copying the same leading byte range preserves bit positions identically to the full copy on any endianness; read-modify-write semantics are unchanged sinceset_unsafewrites back exactly the byte range it read. The opt-inBOOST_GIL_CONFIG_HAS_UNALIGNED_ACCESSpath is left untouched — the bounded overloads are defined outside the#ifand used by all four reference classes regardless.Verification (local, ASan, Boost 1.92)
bit_aligned_image3_type<1,2,1>, 2×1): crashes before (heap-buffer-overflowinstatic_copy_bytesviapacked_dynamic_channel_reference::set_unsafe), runs clean after; written channel values read back correctly.bit_aligned_image3_type<3,5,3>on a 32-bit field, 3×1): crashes before, all 9 channel values correct after.packed_channel_reference<uint16_t,3,5>static path incl. reference-to-reference assignment (set_from_reference):0xBEEF/17 →0xBE8F,0xBEEF,0x1234→0xBE37, exactly as expected.clang++ -std=c++17 -fsanitize=addresson arm64 macOS, no other files touched.