https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112792
--- Comment #2 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by David Malcolm <dmalc...@gcc.gnu.org>: https://gcc.gnu.org/g:5f1bed2a7af828103ca23a3546466a23e8dd2f30 commit r14-6622-g5f1bed2a7af828103ca23a3546466a23e8dd2f30 Author: David Malcolm <dmalc...@redhat.com> Date: Sat Dec 16 09:03:16 2023 -0500 analyzer: use bit-level granularity for concrete bounds-checking [PR112792] PR analyzer/112792 reports false positives from -fanalyzer's bounds-checking on certain packed structs containing bitfields e.g. in the Linux kernel's drivers/dma/idxd/device.c: union msix_perm { struct { u32 rsvd2 : 8; u32 pasid : 20; }; u32 bits; } __attribute__((__packed__)); The root cause is that the bounds-checking is done using byte offsets and ranges; in the above, an access of "pasid" is treated as a 32-bit access starting one byte inside the union, thus accessing byte offsets 1-4 when only offsets 0-3 are valid. This patch updates the bounds-checking to use bit offsets and ranges wherever possible - for concrete offsets and capacities. In the above accessing "pasid" is treated as bits 8-27 of a 32-bit region, fixing the false positive. Symbolic offsets and ranges are still handled at byte granularity. gcc/analyzer/ChangeLog: PR analyzer/112792 * bounds-checking.cc (out_of_bounds::oob_region_creation_event_capacity): Rename "capacity" to "byte_capacity". Layout fix. (out_of_bounds::::add_region_creation_events): Rename "capacity" to "byte_capacity". (class concrete_out_of_bounds): Rename m_out_of_bounds_range to m_out_of_bounds_bits and convert from a byte_range to a bit_range. (concrete_out_of_bounds::get_out_of_bounds_bytes): New. (concrete_past_the_end::concrete_past_the_end): Rename param "byte_bound" to "bit_bound". Initialize m_byte_bound. (concrete_past_the_end::subclass_equal_p): Update for renaming of m_byte_bound to m_bit_bound. (concrete_past_the_end::m_bit_bound): New field. (concrete_buffer_overflow::concrete_buffer_overflow): Convert param "range" from byte_range to bit_range. Rename param "byte_bound" to "bit_bound". (concrete_buffer_overflow::emit): Update for bits vs bytes. (concrete_buffer_overflow::describe_final_event): Split into... (concrete_buffer_overflow::describe_final_event_as_bytes): ...this (concrete_buffer_overflow::describe_final_event_as_bits): ...and this. (concrete_buffer_over_read::concrete_buffer_over_read): Convert param "range" from byte_range to bit_range. Rename param "byte_bound" to "bit_bound". (concrete_buffer_over_read::emit): Update for bits vs bytes. (concrete_buffer_over_read::describe_final_event): Split into... (concrete_buffer_over_read::describe_final_event_as_bytes): ...this (concrete_buffer_over_read::describe_final_event_as_bits): ...and this. (concrete_buffer_underwrite::concrete_buffer_underwrite): Convert param "range" from byte_range to bit_range. (concrete_buffer_underwrite::describe_final_event): Split into... (concrete_buffer_underwrite::describe_final_event_as_bytes): ...this (concrete_buffer_underwrite::describe_final_event_as_bits): ...and this. (concrete_buffer_under_read::concrete_buffer_under_read): Convert param "range" from byte_range to bit_range. (concrete_buffer_under_read::describe_final_event): Split into... (concrete_buffer_under_read::describe_final_event_as_bytes): ...this (concrete_buffer_under_read::describe_final_event_as_bits): ...and this. (region_model::check_region_bounds): Use bits for concrete values, and rename locals to indicate whether we're dealing with bits or bytes. Specifically, replace "num_bytes_sval" with "num_bits_sval", and get it from reg's "get_bit_size_sval". Replace "num_bytes_tree" with "num_bits_tree". Rename "capacity" to "byte_capacity". Rename "cst_capacity_tree" to "cst_byte_capacity_tree". Replace "offset" and "num_bytes_unsigned" with "bit_offset" and "num_bits_unsigned" respectively, converting from byte_offset_t to bit_offset_t. Replace "out" and "read_bytes" with "bits_outside" and "read_bits" respectively, converting from byte_range to bit_range. Convert "buffer" from byte_range to bit_range. Replace "byte_bound" with "bit_bound". * region.cc (region::get_bit_size_sval): New. (offset_region::get_bit_offset): New. (offset_region::get_bit_size_sval): New. (sized_region::get_bit_size_sval): New. (bit_range_region::get_bit_size_sval): New. * region.h (region::get_bit_size_sval): New vfunc. (offset_region::get_bit_offset): New decl. (offset_region::get_bit_size_sval): New decl. (sized_region::get_bit_size_sval): New decl. (bit_range_region::get_bit_size_sval): New decl. * store.cc (bit_range::intersects_p): New, based on byte_range::intersects_p. (bit_range::exceeds_p): New, based on byte_range::exceeds_p. (bit_range::falls_short_of_p): New, based on byte_range::falls_short_of_p. (byte_range::intersects_p): Delete. (byte_range::exceeds_p): Delete. (byte_range::falls_short_of_p): Delete. * store.h (bit_range::intersects_p): New overload. (bit_range::exceeds_p): New. (bit_range::falls_short_of_p): New. (byte_range::intersects_p): Delete. (byte_range::exceeds_p): Delete. (byte_range::falls_short_of_p): Delete. gcc/testsuite/ChangeLog: PR analyzer/112792 * c-c++-common/analyzer/out-of-bounds-pr112792.c: New test. Signed-off-by: David Malcolm <dmalc...@redhat.com>