-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix "out of bounds" undefined behavior #16975
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
Conversation
What error is this fixing? |
This fixes undefined behavior which can be "miscompiled" in future E.g. Here it's perfectly legal for compiler in case of "n = 5" compile as Also it lets users to compile code with -fsanitize=bounds or -fsanitize=bounds-strict. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense to me in this case, but there appears to be a similar construct in agg_pixfmt_rgb.h
and agg_pixfmt_gray.h
for which sizeof(class)
!= step size. I don't think we use rgb
, but we do use gray
for resampling.
pix_step = 4, | ||
pix_width = sizeof(value_type) * pix_step, | ||
}; | ||
struct pixel_type | ||
{ | ||
value_type c[num_components]; | ||
value_type c[pix_step]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should change, as it makes the rgba
file inconsistent with the rgb
one, and semantically these are different things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we change them as well? Looks like this way code is simpler and compliant.
Then num_components is not used anywhere, but I can return it back if you like.
PTAL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@QuLogic : Has this point been resolved? (I don't have any opinion either way, but I wanted to make sure this isn't a blocker.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(For what it's worth, if it is easier to land this overall fix without chaning the bounds here, I'd welcome that as a simpler first step; the deduplication of these two redundant enumerators could be a separate PR.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like as small as possible patches as well. Still here to use "this + 1" we need array size to be the "pix_step" not "num_components". Then num_components is unused. So the best I can do is moving lines with "- num_components = ..." into a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure who you're tagging there; as you can see I've already approved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vitalybuka: Ah of course, Step
could be not 1... say, is the current code correct when Step
is not 1? In that case the current code's next
would skip over some array elements, right?
This is detected with clang and -fsanitizer=bounds. Out-of-bound addressing array of fixed size is undefined behavior in both C and C++. It is so even if underlying memory is properly allocated. Easy fix here is to iterate with this pointer instead of pixel_type::c array.
ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantically, I understand what it was trying to do, even if it's not up to the standard, but in practice, dropping the different sizes shouldn't be a problem.
In practice it's inconvenience for those who compile with -fsanitize=bounds. We will have to This check finds real buffer overflow which can't be detected by other means. E.g. -fsanitize=address will not report buffer overflow reads from a different field of the same structure. Also I think my last patch makes code a little bit nice even regardless to the bounds :) |
Yes, it would break |
Can someone merge this please? |
Merging requires 2 approvals. Please be patient. |
I would need far more context to approve. What is this c code used by, and how do we know that this change works? |
@jklymak: Is there anything I could do to help? I'm quite keen on having this fixed. So... first off, is it contentious whether the status quo is buggy? (It is accessing an array out of bounds.) Is the fix correct? If you consider why the buggy code "seems to work", it's because it is using the address of the first subobject and casting it to the address of the object itself, so we can replace the cast by just It seems that what is meant here is to itereate through an array of I've applied this change internally (minus the change of the array bound), and some of our tests continue to pass, and the previous bug (as reported by sanitizers) is fixed. (I'm not sure how much coverage we have, though. I can run some more tests.) |
A correction to my previous comment: The stride logic is only correct for the "rgba" version, where |
|
@QuLogic: If |
Why do you keep pinging a random other person? I don't think they appreciate it. The point of |
@QuLogic: Oh, so sorry, I had been completely blind to the mixup! I used the auto completion and selected the wrong user! Hopefully fixed now. |
@QuLogic: aha -- so do you mean that you really have a large array |
Yes, it's somewhat like BMP (maybe JPEG too?) where every row must be on a 4-byte boundary, even if width is not divisible by 4. |
But note this is mostly academic, as we never use rgb, and always use |
Oh, interesting -- could you maybe back up a bit and explain the intended layout from scratch? So, let's say for example for RGB, we have a contiguous buffer |
@QuLogic: Oh, is that right? Could we maybe enshrine this in code and remove the |
Well, we aren't upstream, and while upstream is somewhat dormant, it's fine to fix bugs, but changing semantics seems unnecessary. |
OK, so if we're not changing that, but only the pointer arithmetic, then I suppose the |
No, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to make the biggest actually difference when we are laying this on top of a buffer with a pixel stride that is bigger than the pixel size so
'RGBxRGBx' (for an 4byte aligned array with 3byte actual data payload or 'WxxxWxxx' for 4byte aligned pixels with 1byte data) where previously the c
component of the struct would be the size of the actual payload, where as now it will be the full size of stride.
I think any further discussion about overhauling this code should be pushed to the apparently revived Agg upstream.
Thank you for the contribution! |
This is detected with clang and -fsanitizer=bounds.
Out-of-bound addressing array of fixed size is undefined
behavior in both C and C++. It is so even if underlying
memory is properly allocated.
Easy fix here is to iterate with this pointer instead of
pixel_type::c array.