-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
avoid variable-length-array syntax that trip up strict-bound UBSAN #15460
Conversation
I've found at least those code pieces that seem to consider extra element in the struct and must be addressed:
and
There may be more, but I just haven't looked deeper. |
Oh sure, that I even noticed that The |
Hmm, OK I re-read up on flexible array members (FAM), and tbh. my C times are a bit in the past and I have spearheaded this a bit too quickly due to ignoring that, sorry. For ISO/IEC 9899:1999 (TC2) §6.7.2.1 ¶16:
So I think that the data_start = (void *)P2ROUNDUP(((uintptr_t)hdr +
offsetof(sa_hdr_phys_t, sa_lengths) +
(sizeof (uint16_t) * tb->lot_var_sizes)), 8); |
it is not my strongest area, but IMHO both should be identical. |
After a night of sleep and some coffee it's obvious (again) that you're right, I'll try to rethink things before shooting from my hips again, my C time are simple a bit to far in the past to do so. For the quoted code |
I have encountered this issue on 6.5 as well, and this patch has cleared it up for me. |
Is this something |
@lundman I'm going to test that out (unless someone beats me to it!) |
Use the modern, and more common C99 syntax to declare flexible array members instead of the fixed-size array with a length of one. While the latter works in C just fine, with some extra handling for the extra space in length/array-size calculations, it's making it impossible to distinguish an out-of-bound access fro a variable array access. Why now? Linux 6.5 made it's Undefined Behavior Sanitizer (UBSAN) integration more strict with commit 2d47c6956ab3 ("ubsan: Tighten UBSAN_BOUNDS on GCC"), causing quite some oopses with "UBSAN: array-index-out-of-bounds" errors logged on boot when that kernel is used and UBSAN_BOUNDS check is enabled. The change to modern VLA syntax does not affect the data layout whatsoever, but can result in a smaller container size as reported by the `sizeof` operator, basically the size reported by `sizeof (struct foo)` will be now the same as the one from `offsetof(struct foo, flexible_array_member)`. So we need to carefully check all call-sites that use the changed struct in sizeof calculations, especially when calculating the full data length the base-struct plus the data in the flexible array occupy, because previously one had to bias that by -1 previously, as the fixed-sized array used to model flexible arrays, provided space for one in the struct directly. I tried to checked each changed struct for potential breakage, some short notes (size differences are always for x86_64 with gcc 12, but as types are consisting of fixed bit width ones it shouldn't matter): - dk_gpt_t is an internal struct and doesn't seem to be written directly to disk as is. But, there are a few places where the length calculation needs to be adjusted, as struct dk_gpt doesn't holds a fixed extra element anymore that previously had to be accounted for. - sa_hdr_phys_t stays the same size due to padding, but the code using its sizeof value for overall length calculation needs to drop the -1 bias for the length, e.g., when multiplying to get total size. - mzap_ent_t shrinks from 128 to 64 bytes, it is not used in sizeof calculations anywhere FWICT. - zap_leaf_phys_t shrinks from 56 to 48 bytes but isn't used in sizeof calculations, and the l_hash entry count is calculated explicitly from the block-size / 2^5 (= 32). Link: openzfs#15145 Signed-off-by: Thomas Lamprecht <[email protected]>
7337d2e
to
b08811f
Compare
I put out an alternate PR that disables CONFIG_UBSAN for the files that use those cursed size-1-arrays at the end of structs: #15510 Note: it doesn't do anything for the EFI files since those aren't built into the kernel. @lundman turns out |
Ah interesting. I wonder what Microsoft will do, as ALL their structs have [1] in them, hundreds :) |
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.
@ThomasLamprecht thanks for iterating on this. I think the approach you've taken here is what we're going to need to do to get UBSAN working long term. Since these are on disk structures we're going to need to be careful are take our time. In the meanwhile, it'd be great if you could test out the workaround in #15510 which suppresses the checks.
@@ -265,7 +265,7 @@ struct sa_handle { | |||
|
|||
#define SA_HDR_SIZE_MATCH_LAYOUT(hdr, tb) \ | |||
(SA_HDR_SIZE(hdr) == (sizeof (sa_hdr_phys_t) + \ | |||
(tb->lot_var_sizes > 1 ? P2ROUNDUP((tb->lot_var_sizes - 1) * \ | |||
(tb->lot_var_sizes > 1 ? P2ROUNDUP(tb->lot_var_sizes * \ |
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.
Depending on how much padding was added to the sa_hdr_phys
structure when sa_lengths[1]
we may need to add a special case for tb->lot_var_sizes == 0
here. This is an on-disk struct and we need to be careful that the updated SA_HDR_SIZE_MATCH_LAYOUT
returns the same sizes as the old code would have. Changing the conditional might be enough depending on the old padding.
(tb->lot_var_sizes > 1 ? P2ROUNDUP(tb->lot_var_sizes * \ | |
(tb->lot_var_sizes > 0 ? P2ROUNDUP(tb->lot_var_sizes * \ |
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.
Changing to tb->lot_var_sizes > 0
appears to be sufficient to fix this. I agree that it is necessary.
@behlendorf Thanks for your comments! I actually wanted to write a short message about my update when I force-pushed, but I got side-tracked too much, sorry. Anyhow, my last push is still not complete and leads to actual boot-hangs (hung task on I plan to also split up my PR into a separate commit per struct change, and possibly then also a separate PR (depending on what you favor?), that way we could get each change to a struct in separately once deemed ready. I now also tested the workaround from #15510 and it works fine, like I commented there. |
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from #15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue #15145 Closes #15510
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from openzfs#15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue openzfs#15145 Closes openzfs#15510
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.
@@ -265,7 +265,7 @@ struct sa_handle { | |||
|
|||
#define SA_HDR_SIZE_MATCH_LAYOUT(hdr, tb) \ | |||
(SA_HDR_SIZE(hdr) == (sizeof (sa_hdr_phys_t) + \ | |||
(tb->lot_var_sizes > 1 ? P2ROUNDUP((tb->lot_var_sizes - 1) * \ | |||
(tb->lot_var_sizes > 1 ? P2ROUNDUP(tb->lot_var_sizes * \ |
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.
Changing to tb->lot_var_sizes > 0
appears to be sufficient to fix this. I agree that it is necessary.
Closing for now since #15510 is in place. @ThomasLamprecht feel free to open a new PR to resolve these when you have time to work on it again. |
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from openzfs#15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue openzfs#15145 Closes openzfs#15510
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from openzfs#15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue openzfs#15145 Closes openzfs#15510
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from openzfs#15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue openzfs#15145 Closes openzfs#15510
This gets around UBSAN errors when using arrays at the end of structs. It converts some zero-length arrays to variable length arrays and disables UBSAN checking on certain modules. It is based off of the patch from #15460. Reviewed-by: Brian Behlendorf <[email protected]> Tested-by: Thomas Lamprecht <[email protected]> Co-authored-by: Thomas Lamprecht <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Issue #15145 Closes #15510
Motivation and Context
This fixes #15145 i.e., a stricter bound checking when UBSAN is enabled for Linux since 6.5.
Disabling UBSAN is not really an option for us, and the "modern" (C99) VLA syntax is so widely used, that using it makes IMO the code a tiny bit more readable as a side effect.
Description
Use the more common C99 syntax to declare variable length arrays, while older ones, where a (fake) array length of one was declared, works too in C, it's making it impossible to distinguish an out-of-bound access for a variable array access.
How Has This Been Tested?
I have built ZFS 2.2 with this patch for the 6.5 kernel, then I
In #15145 (comment) it's mentioned that there could be some potential regression due to change in size that the structs report through the
sizeof
operator, I tried to check the changed structs usage in-depth, and commented on them in the commit message, but as I'm not that familiar with the code base I might have missed some things.If anybody knows better tests to ensure compatibility w.r.t. the structs touched I'd be glad to hear them, and try to execute/implement them.
Types of changes
Checklist:
Signed-off-by
.