Skip to content

SPP prefetcher OOB bug #668

Open
Open

Description

@jiegec

Describe the bug
The SPP prefetcher in prefetcher/spp_dev has out of bounds memory write:

[VMEM] WARNING: physical memory size is smaller than virtual memory size.

*** ChampSim Multicore Out-of-Order Simulator ***
Warmup Instructions: 200000000
Simulation Instructions: 500000000
Number of CPUs: 1
Page size: 4096

Initialize SIGNATURE TABLE
ST_SET: 1
ST_WAY: 256
ST_TAG_BIT: 16

Initialize PATTERN TABLE
PT_SET: 512
PT_WAY: 4
SIG_DELTA_BIT: 7
C_SIG_BIT: 4
C_DELTA_BIT: 4

Initialize PREFETCH FILTER
FILTER_SET: 1024
=================================================================
==958115==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5060002dd6e0 at pc 0x55e2f37a5336 bp 0x7ffe046055f0 sp 0x7ffe046055e8
WRITE of size 4 at 0x5060002dd6e0 thread T0
    #0 0x55e2f37a5335 in spp_dev::PATTERN_TABLE::read_pattern(unsigned int, std::vector<long, std::allocator<long> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, unsigned int&, unsigned int&, unsigned int&, unsigned int&) prefetcher/spp_dev/spp_dev.cc:363
    #1 0x55e2f37a97f6 in spp_dev::prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, unsigned char, bool, access_type, unsigned int) prefetcher/spp_dev/spp_dev.cc:73
    #2 0x55e2f359c3d0 in auto CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda(auto:1&)#1}::operator()<spp_dev>(spp_dev&) const /home/jiegec/ChampSim/inc/cache.h:355
    #3 0x55e2f359c3d0 in auto CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}::operator()<spp_dev>(spp_dev&) const /home/jiegec/ChampSim/inc/cache.h:368
    #4 0x55e2f359c3d0 in unsigned int std::__invoke_impl<unsigned int, CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>(std::__invoke_other, CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, spp_dev&) /usr/include/c++/14/bits/invoke.h:61
    #5 0x55e2f359c3d0 in std::__invoke_result<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>::type std::__invoke<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, spp_dev&) /usr/include/c++/14/bits/invoke.h:96
    #6 0x55e2f359c3d0 in decltype(auto) std::__apply_impl<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, std::tuple<spp_dev>&, 0ul>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, std::tuple<spp_dev>&, std::integer_sequence<unsigned long, 0ul>) /usr/include/c++/14/tuple:2923
    #7 0x55e2f359c3d0 in decltype(auto) std::apply<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, std::tuple<spp_dev>&>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, std::tuple<spp_dev>&) /usr/include/c++/14/tuple:2938
    #8 0x55e2f359c3d0 in CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int) /home/jiegec/ChampSim/inc/cache.h:368
    #9 0x55e2f3481d22 in CACHE::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int) const src/cache.cc:800
    #10 0x55e2f3481d22 in CACHE::try_hit(CACHE::tag_lookup_type const&) src/cache.cc:268
    #11 0x55e2f349394e in operator()<CACHE::tag_lookup_type> src/cache.cc:511
    #12 0x55e2f349394e in operator()<std::_Deque_iterator<CACHE::tag_lookup_type, CACHE::tag_lookup_type&, CACHE::tag_lookup_type*> > /usr/include/c++/14/bits/predefined_ops.h:395
    #13 0x55e2f349394e in __find_if<std::_Deque_iterator<CACHE::tag_lookup_type, CACHE::tag_lookup_type&, CACHE::tag_lookup_type*>, __gnu_cxx::__ops::_Iter_negate<CACHE::operate()::<lambda(const auto:49&)> > > /usr/include/c++/14/bits/stl_algobase.h:2137
    #14 0x55e2f349394e in __find_if_not<std::_Deque_iterator<CACHE::tag_lookup_type, CACHE::tag_lookup_type&, CACHE::tag_lookup_type*>, __gnu_cxx::__ops::_Iter_pred<CACHE::operate()::<lambda(const auto:49&)> > > /usr/include/c++/14/bits/stl_algo.h:114
    #15 0x55e2f349394e in __stable_partition<std::_Deque_iterator<CACHE::tag_lookup_type, CACHE::tag_lookup_type&, CACHE::tag_lookup_type*>, __gnu_cxx::__ops::_Iter_pred<CACHE::operate()::<lambda(const auto:49&)> > > /usr/include/c++/14/bits/stl_algo.h:1530
    #16 0x55e2f34b2736 in stable_partition<std::_Deque_iterator<CACHE::tag_lookup_type, CACHE::tag_lookup_type&, CACHE::tag_lookup_type*>, CACHE::operate()::<lambda(const auto:49&)> > /usr/include/c++/14/bits/stl_algo.h:1579
    #17 0x55e2f34b2736 in CACHE::operate() src/cache.cc:511
    #18 0x55e2f36b0fd2 in champsim::operable::_operate() src/operable.cc:36
    #19 0x55e2f36b0fd2 in champsim::operable::operate_on(champsim::chrono::clock const&) src/operable.cc:27
    #20 0x55e2f34e6f4e in champsim::do_cycle(champsim::environment&, std::vector<champsim::tracereader, std::allocator<champsim::tracereader> >&, std::vector<unsigned long, std::allocator<unsigned long> >, champsim::chrono::clock&) src/champsim.cc:49
    #21 0x55e2f34ec25b in champsim::do_phase(champsim::phase_info const&, champsim::environment&, std::vector<champsim::tracereader, std::allocator<champsim::tracereader> >&, champsim::chrono::clock&) src/champsim.cc:91
    #22 0x55e2f3426002 in champsim::main(champsim::environment&, std::vector<champsim::phase_info, std::allocator<champsim::phase_info> >&, std::vector<champsim::tracereader, std::allocator<champsim::tracereader> >&) src/champsim.cc:193
    #23 0x55e2f3430636 in main src/main.cc:123
    #24 0x7f09275c4ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #25 0x7f09275c4d64 in __libc_start_main_impl ../csu/libc-start.c:360
    #26 0x55e2f3431e40 in _start (/home/jiegec/ChampSim/bin/champsim+0x48e40) (BuildId: a5788edfb02cf186dbf7de56ad176a03450be9c5)

0x5060002dd6e0 is located 0 bytes after 64-byte region [0x5060002dd6a0,0x5060002dd6e0)
allocated by thread T0 here:
    #0 0x7f0927c09778 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:95
    #1 0x55e2f37a9191 in std::__new_allocator<unsigned int>::allocate(unsigned long, void const*) /usr/include/c++/14/bits/new_allocator.h:151
    #2 0x55e2f37a9191 in std::allocator_traits<std::allocator<unsigned int> >::allocate(std::allocator<unsigned int>&, unsigned long) /usr/include/c++/14/bits/alloc_traits.h:515
    #3 0x55e2f37a9191 in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_M_allocate(unsigned long) /usr/include/c++/14/bits/stl_vector.h:380
    #4 0x55e2f37a9191 in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_M_create_storage(unsigned long) /usr/include/c++/14/bits/stl_vector.h:398
    #5 0x55e2f37a9191 in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_Vector_base(unsigned long, std::allocator<unsigned int> const&) /usr/include/c++/14/bits/stl_vector.h:334
    #6 0x55e2f37a9191 in std::vector<unsigned int, std::allocator<unsigned int> >::vector(unsigned long, std::allocator<unsigned int> const&) /usr/include/c++/14/bits/stl_vector.h:557
    #7 0x55e2f37a9191 in spp_dev::prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, unsigned char, bool, access_type, unsigned int) prefetcher/spp_dev/spp_dev.cc:37
    #8 0x55e2f359c3d0 in auto CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda(auto:1&)#1}::operator()<spp_dev>(spp_dev&) const /home/jiegec/ChampSim/inc/cache.h:355
    #9 0x55e2f359c3d0 in auto CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}::operator()<spp_dev>(spp_dev&) const /home/jiegec/ChampSim/inc/cache.h:368
    #10 0x55e2f359c3d0 in unsigned int std::__invoke_impl<unsigned int, CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>(std::__invoke_other, CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, spp_dev&) /usr/include/c++/14/bits/invoke.h:61
    #11 0x55e2f359c3d0 in std::__invoke_result<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>::type std::__invoke<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, spp_dev&>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, spp_dev&) /usr/include/c++/14/bits/invoke.h:96
    #12 0x55e2f359c3d0 in decltype(auto) std::__apply_impl<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, std::tuple<spp_dev>&, 0ul>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, std::tuple<spp_dev>&, std::integer_sequence<unsigned long, 0ul>) /usr/include/c++/14/tuple:2923
    #13 0x55e2f359c3d0 in decltype(auto) std::apply<CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}, std::tuple<spp_dev>&>(CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int)::{lambda((auto:1&)...)#1}&&, std::tuple<spp_dev>&) /usr/include/c++/14/tuple:2938
    #14 0x55e2f359c3d0 in CACHE::prefetcher_module_model<spp_dev>::impl_prefetcher_cache_operate(champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, champsim::address_slice<champsim::static_extent<(champsim::data::bits)64, (champsim::data::bits)0> >, bool, bool, access_type, unsigned int) /home/jiegec/ChampSim/inc/cache.h:368

SUMMARY: AddressSanitizer: heap-buffer-overflow prefetcher/spp_dev/spp_dev.cc:363 in spp_dev::PATTERN_TABLE::read_pattern(unsigned int, std::vector<long, std::allocator<long> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, unsigned int&, unsigned int&, unsigned int&, unsigned int&)
Shadow bytes around the buggy address:
  0x5060002dd400: 00 00 00 00 00 00 00 00 fa fa fa fa fd fd fd fd
  0x5060002dd480: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x5060002dd500: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
  0x5060002dd580: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
  0x5060002dd600: fd fd fd fd fa fa fa fa 00 00 00 00 00 00 00 00
=>0x5060002dd680: fa fa fa fa 00 00 00 00 00 00 00 00[fa]fa fa fa
  0x5060002dd700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x5060002dd780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x5060002dd800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x5060002dd880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x5060002dd900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==958115==ABORTING

To Reproduce

Steps to reproduce the behavior:

  1. Patch the code to use spp_dev and enable ASan:
diff --git a/Makefile b/Makefile
index 6aae9c9c..818953a4 100644
--- a/Makefile
+++ b/Makefile
@@ -16,9 +16,9 @@ override REPLACEMENT_ROOT += $(addsuffix /replacement,$(MODULE_ROOT))
 
 # vcpkg integration
 TRIPLET_DIR = $(patsubst %/,%,$(firstword $(filter-out $(ROOT_DIR)/vcpkg_installed/vcpkg/, $(wildcard $(ROOT_DIR)/vcpkg_installed/*/))))
-override CPPFLAGS += -I$(OBJ_ROOT)
+override CPPFLAGS += -I$(OBJ_ROOT) -g -fsanitize=address
 override LDFLAGS  += -L$(TRIPLET_DIR)/lib -L$(TRIPLET_DIR)/lib/manual-link
-override LDLIBS   += -lCLI11 -llzma -lz -lbz2 -lfmt
+override LDLIBS   += -lCLI11 -llzma -lz -lbz2 -lfmt -fsanitize=address
 
 .PHONY: all clean compile_commands compile_commands_clean configclean test pytest maketest
 
diff --git a/champsim_config.json b/champsim_config.json
index d8a33e13..28f14b48 100644
--- a/champsim_config.json
+++ b/champsim_config.json
@@ -68,7 +68,7 @@
     "prefetch_as_load": false,
     "virtual_prefetch": false,
     "prefetch_activate": "LOAD,PREFETCH",
-    "prefetcher": "no"
+    "prefetcher": "spp_dev"
   },
 
   "L2C": {
  1. Compile and run:
./config.sh champsim_config.json -v
make -j
bin/champsim --warmup-instructions 200000000 --simulation-instructions 500000000 600.perlbench_s-210B.champsimtrace.xz

Expected behavior
It should not crash.

Output
Pasted above.

Desktop (please complete the following information):

  • OS: Linux
  • Compiler: GCC
  • Version: Latest master commit 06de8d3

Additional context

The problem lies in pf_q_tail: it increases without bounds checking.

Activity

  1. ngober commented on Nov 25, 2025

    @ngober
    Collaborator

    This looks like it could be fixed if confidence_q was made into a std::vector. The problem here is that we really shouldn't be using arrays passed as pointers without any management.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions