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>

Reply via email to