Public bug reported:

A Bus error always occurs after reading or writing a specific number of
unique locations from/to a shared memory pool which is created using
shm_open() and mmap(). It does not matter if the pool memory locations
are accessed starting at the base of the pool, incrementing up through
the addresses, or starting at the top of the pool, decrementing down
through the addresses. The count of unique locations accessed before the
Bus error occurs is the same repeatable value. The count value is close
to but not exactly 1/2 of the total system memory.

The count is of unique locations accessed. If an address range less than
the failure count is accessed repeatedly, the Bus error does not occur.

The unique addresses do not have to be accessed sequentially to cause
the Bus error. While this aspect has not been tested exhaustively, if a
range of addresses are jumped over, the Bus error still occurs. Note
that the failure count is slightly different than if the addresses are
accessed sequentially.

This error is consistently repeatable on the following 3 systems:
Ubuntu 22.04.3 LTS
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
Machine: Amazon EC2
CPU: AMD EPYC 7571
Memory: 124.68 GiB

Distro: Linux Mint 20  base: Ubuntu 20.04
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04)
Machine: Dell System XPS L502X
CPU: Intel Core i7-2620M
Total Memory: 8,219,435,008 bytes

Distro: Linux Mint 21 base: Ubuntu 22.04
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
Machine: Dell Inspiron 5558
CPU: Intel Core i3-5005U
Total Memory: 8,229,298,176 bytes

C++ Test program that demonstrates the issue.
---------------------------------------------
// September 2023 - Gene Weber

// This test program generates a Bus error core dump after reading or writing a 
specific number
// of unique locations from/to a shared memory pool. It does not matter if the 
pool memory locations
// are accessed starting at the base of the pool, incrementing up through the 
addresses, or starting
// at the top of the pool, decrementing down through the addresses. The count 
of unique locations
// accessed before the Bus error occurs is the same repeatable value. The count 
value is close to
// but not exactly 1/2 of the total system memory.
//
// The count is of unique locations accessed. If an address range less than the 
failure count is
// accessed repeatedly, the Bus error does not occur.
//
// The unique addresses do not have to be accessed sequentially to cause the 
Bus error. While this
// has not been tested exhaustively, if a range of addresses are jumped over, 
the Bus error still
// occurs. Note that the failure count is slightly different than if the 
addresses are accessed
// sequentially.
//
// This test program has command line options to allow testing these different 
scenarios.

// Compiling with either has the same results:
// g++ -std=c++11 test.cpp -W -Wall -Wextra -pedantic -pthread -o test -lrt
// g++ -std=c++20 -O3 test.cpp -W -Wall -Wextra -pedantic -pthread -o test -lrt

#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <math.h>

int main(int argc, char** argv) {
    int page_size = 4096;
    
    if (argc != 5) {
        std::cerr << "\nUsage: ./test <total_system_ram_in_bytes> <u/d> <r/w> 
<j/r/x> \n\n";
        std::cerr << "       <total_system_ram_in_bytes> = total from \"free 
-b\".\n";
        std::cerr << "       <u/d> = u to index addresses up from bottom of 
pool, d for down from top.\n";
        std::cerr << "       <r/w> = r to read indexed address, w for write.\n";
        std::cerr << "       <j/r/x> = j for jump index by 5% of total, r for 
limit index range to 1/2 of total, x linear indexing.\n\n";
        exit(EXIT_FAILURE);
    }

    uint_fast64_t total_system_ram = strtoull(argv[1], NULL, 10);

    // Set pool_size to a whole number of pages that is ~60% of system memory.
    uint_fast64_t pool_size = total_system_ram * 0.6;
    pool_size = pool_size - (pool_size % page_size);
    std::cout << "pool size = " << pool_size << "\n";

    // Create a mask to print status at intervals of ~1/20 of the pool size.
    uint_fast64_t print_interval = pow(2,ceil(log2(pool_size/20))) - 1;
    // Create a  value to start printing all addresses after a few pages less 
than
    // 1/2 of the pool size has been accessed.
        uint_fast64_t almost_half = (total_system_ram/2) - (3 * page_size);

    // Create a "jump address index" value to use if the jump option is 
selected.
    uint_fast64_t jmp_indx = total_system_ram * 0.05;
    if (*argv[4] == 'j') {
        std::cout << "jump address index by " << jmp_indx << "\n";
    }

    // Create an "address index range" value to use if range limit option is 
selected.
    uint_fast64_t indx_range = pool_size / 2;
    if (*argv[4] == 'r') {
        std::cout << "address index range will be limited to " << indx_range << 
" locations.\n";
    }

    int fd;
    std::string shmpath = "/foo";

    // Remove any existing shared memory object
    shm_unlink(shmpath.c_str());
    // Create the shared memory object with read-write access.
    fd = shm_open(shmpath.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | 
S_IWUSR);
    
    if (fd == -1) {
        std::cerr << "\nshm_open shmbuf failure. Exiting program.\n\n";
        exit(EXIT_FAILURE);
    }
    
    // Truncate (set) the size.
    if (ftruncate64(fd, pool_size) == -1) {
        std::cerr << "\nftruncate shmbuf failure. Exiting program.\n\n";
        exit(EXIT_FAILURE);
    }
    
    // Map the shared memory object.
    char* pool = (char*)mmap(NULL, pool_size, PROT_READ | PROT_WRITE, 
MAP_SHARED, fd, 0);
    if (pool == MAP_FAILED) {
        std::cerr << "\nmmap pool failure. Exiting program.\n\n";
        exit(EXIT_FAILURE);
    }

    std::cout << "pool base address = " << (uint_fast64_t)pool << "\n";
    std::cout << "pool top address = " << (uint_fast64_t)pool + pool_size << 
"\n";

    char temp = 'a';
    uint_fast64_t indx = 0, prnt_num = 0;

    // Increment a count from 0 to pool_size-1. The count of locations accessed 
seems to be
    // the issue, not the location accessed.
    // Note: All of the control statements and logic in the loop impact the 
performance of
    // this test. This could be 12 unique test programs that run faster, but 
this seemed
    // like a better trade off.
    for (uint_fast64_t count=0; count<pool_size; count++) {

        // If argument 2 is "u" access locations from the bottom of the pool 
up, otherwise
        // access locations from the top of the pool down.
        indx = *argv[2] == 'u' ? count : (pool_size - 1) - count;
        // If argument 4 is "r", limit the address index to a range of 
indx_range locations.
        if (*argv[4] == 'r') {
            indx = *argv[2] == 'u' ? count % indx_range : (pool_size - 1) - 
(count % indx_range);
        }
        // If argument 4 is "j", and roughly 1/4 of the pool has been accessed, 
jump the
        // address index by the amount specified in jmp_indx.
        else if ((*argv[4] == 'j') && (prnt_num > 5)) {
            indx = *argv[2] == 'u' ? indx + jmp_indx : indx - jmp_indx;
        }

        // Print status at intervals using the mask created above.
        if ((count & print_interval) == print_interval) {
            std::cout << "count = " << count << "  Address = " << 
(uint_fast64_t)&pool[indx] << "\n";
            prnt_num++;
        }

        // When a few pages less than 1/2 of system memory has been accessed, 
print all.
        // Dissable if address index range is limited, because no failure will 
occur.
        else if ((count > almost_half) && (*argv[4] != 'r')) {
            std::cout << "count = " << count << "  Address = " << 
(uint_fast64_t)&pool[indx] << "\n";
            }

        // If argument 3 is "r" read the location, otherwise write the location.
        if (*argv[3] == 'r') {
            temp = pool[indx];
        }
        else {
            pool[indx] = temp;
        }
    }
    std::cout << "Success!\n";
}

** Affects: linux (Ubuntu)
     Importance: Undecided
         Status: Incomplete

** Attachment added: "apport image"
   
https://bugs.launchpad.net/bugs/2034607/+attachment/5698056/+files/apport.linux-image-6.2.0-1011-aws.1up8ecgk.apport

-- 
You received this bug notification because you are a member of Kernel
Packages, which is subscribed to linux in Ubuntu.
https://bugs.launchpad.net/bugs/2034607

Title:
  Bus error after reading or writing a specific number of unique
  locations from/to a shared memory pool.

Status in linux package in Ubuntu:
  Incomplete

Bug description:
  A Bus error always occurs after reading or writing a specific number
  of unique locations from/to a shared memory pool which is created
  using shm_open() and mmap(). It does not matter if the pool memory
  locations are accessed starting at the base of the pool, incrementing
  up through the addresses, or starting at the top of the pool,
  decrementing down through the addresses. The count of unique locations
  accessed before the Bus error occurs is the same repeatable value. The
  count value is close to but not exactly 1/2 of the total system
  memory.

  The count is of unique locations accessed. If an address range less
  than the failure count is accessed repeatedly, the Bus error does not
  occur.

  The unique addresses do not have to be accessed sequentially to cause
  the Bus error. While this aspect has not been tested exhaustively, if
  a range of addresses are jumped over, the Bus error still occurs. Note
  that the failure count is slightly different than if the addresses are
  accessed sequentially.

  This error is consistently repeatable on the following 3 systems:
  Ubuntu 22.04.3 LTS
  gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
  Machine: Amazon EC2
  CPU: AMD EPYC 7571
  Memory: 124.68 GiB

  Distro: Linux Mint 20  base: Ubuntu 20.04
  gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04)
  Machine: Dell System XPS L502X
  CPU: Intel Core i7-2620M
  Total Memory: 8,219,435,008 bytes

  Distro: Linux Mint 21 base: Ubuntu 22.04
  gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
  Machine: Dell Inspiron 5558
  CPU: Intel Core i3-5005U
  Total Memory: 8,229,298,176 bytes

  C++ Test program that demonstrates the issue.
  ---------------------------------------------
  // September 2023 - Gene Weber

  // This test program generates a Bus error core dump after reading or writing 
a specific number
  // of unique locations from/to a shared memory pool. It does not matter if 
the pool memory locations
  // are accessed starting at the base of the pool, incrementing up through the 
addresses, or starting
  // at the top of the pool, decrementing down through the addresses. The count 
of unique locations
  // accessed before the Bus error occurs is the same repeatable value. The 
count value is close to
  // but not exactly 1/2 of the total system memory.
  //
  // The count is of unique locations accessed. If an address range less than 
the failure count is
  // accessed repeatedly, the Bus error does not occur.
  //
  // The unique addresses do not have to be accessed sequentially to cause the 
Bus error. While this
  // has not been tested exhaustively, if a range of addresses are jumped over, 
the Bus error still
  // occurs. Note that the failure count is slightly different than if the 
addresses are accessed
  // sequentially.
  //
  // This test program has command line options to allow testing these 
different scenarios.

  // Compiling with either has the same results:
  // g++ -std=c++11 test.cpp -W -Wall -Wextra -pedantic -pthread -o test -lrt
  // g++ -std=c++20 -O3 test.cpp -W -Wall -Wextra -pedantic -pthread -o test 
-lrt

  #include <iostream>
  #include <sys/mman.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <math.h>

  int main(int argc, char** argv) {
      int page_size = 4096;
      
      if (argc != 5) {
          std::cerr << "\nUsage: ./test <total_system_ram_in_bytes> <u/d> <r/w> 
<j/r/x> \n\n";
          std::cerr << "       <total_system_ram_in_bytes> = total from \"free 
-b\".\n";
          std::cerr << "       <u/d> = u to index addresses up from bottom of 
pool, d for down from top.\n";
          std::cerr << "       <r/w> = r to read indexed address, w for 
write.\n";
          std::cerr << "       <j/r/x> = j for jump index by 5% of total, r for 
limit index range to 1/2 of total, x linear indexing.\n\n";
          exit(EXIT_FAILURE);
      }

      uint_fast64_t total_system_ram = strtoull(argv[1], NULL, 10);

      // Set pool_size to a whole number of pages that is ~60% of system memory.
      uint_fast64_t pool_size = total_system_ram * 0.6;
      pool_size = pool_size - (pool_size % page_size);
      std::cout << "pool size = " << pool_size << "\n";

      // Create a mask to print status at intervals of ~1/20 of the pool size.
      uint_fast64_t print_interval = pow(2,ceil(log2(pool_size/20))) - 1;
      // Create a  value to start printing all addresses after a few pages less 
than
      // 1/2 of the pool size has been accessed.
        uint_fast64_t almost_half = (total_system_ram/2) - (3 * page_size);

      // Create a "jump address index" value to use if the jump option is 
selected.
      uint_fast64_t jmp_indx = total_system_ram * 0.05;
      if (*argv[4] == 'j') {
          std::cout << "jump address index by " << jmp_indx << "\n";
      }

      // Create an "address index range" value to use if range limit option is 
selected.
      uint_fast64_t indx_range = pool_size / 2;
      if (*argv[4] == 'r') {
          std::cout << "address index range will be limited to " << indx_range 
<< " locations.\n";
      }

      int fd;
      std::string shmpath = "/foo";

      // Remove any existing shared memory object
      shm_unlink(shmpath.c_str());
      // Create the shared memory object with read-write access.
      fd = shm_open(shmpath.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | 
S_IWUSR);
      
      if (fd == -1) {
          std::cerr << "\nshm_open shmbuf failure. Exiting program.\n\n";
          exit(EXIT_FAILURE);
      }
      
      // Truncate (set) the size.
      if (ftruncate64(fd, pool_size) == -1) {
          std::cerr << "\nftruncate shmbuf failure. Exiting program.\n\n";
          exit(EXIT_FAILURE);
      }
      
      // Map the shared memory object.
      char* pool = (char*)mmap(NULL, pool_size, PROT_READ | PROT_WRITE, 
MAP_SHARED, fd, 0);
      if (pool == MAP_FAILED) {
          std::cerr << "\nmmap pool failure. Exiting program.\n\n";
          exit(EXIT_FAILURE);
      }

      std::cout << "pool base address = " << (uint_fast64_t)pool << "\n";
      std::cout << "pool top address = " << (uint_fast64_t)pool + pool_size << 
"\n";

      char temp = 'a';
      uint_fast64_t indx = 0, prnt_num = 0;

      // Increment a count from 0 to pool_size-1. The count of locations 
accessed seems to be
      // the issue, not the location accessed.
      // Note: All of the control statements and logic in the loop impact the 
performance of
      // this test. This could be 12 unique test programs that run faster, but 
this seemed
      // like a better trade off.
      for (uint_fast64_t count=0; count<pool_size; count++) {

          // If argument 2 is "u" access locations from the bottom of the pool 
up, otherwise
          // access locations from the top of the pool down.
          indx = *argv[2] == 'u' ? count : (pool_size - 1) - count;
          // If argument 4 is "r", limit the address index to a range of 
indx_range locations.
          if (*argv[4] == 'r') {
              indx = *argv[2] == 'u' ? count % indx_range : (pool_size - 1) - 
(count % indx_range);
          }
          // If argument 4 is "j", and roughly 1/4 of the pool has been 
accessed, jump the
          // address index by the amount specified in jmp_indx.
          else if ((*argv[4] == 'j') && (prnt_num > 5)) {
              indx = *argv[2] == 'u' ? indx + jmp_indx : indx - jmp_indx;
          }

          // Print status at intervals using the mask created above.
          if ((count & print_interval) == print_interval) {
              std::cout << "count = " << count << "  Address = " << 
(uint_fast64_t)&pool[indx] << "\n";
              prnt_num++;
          }

          // When a few pages less than 1/2 of system memory has been accessed, 
print all.
          // Dissable if address index range is limited, because no failure 
will occur.
          else if ((count > almost_half) && (*argv[4] != 'r')) {
              std::cout << "count = " << count << "  Address = " << 
(uint_fast64_t)&pool[indx] << "\n";
            }

          // If argument 3 is "r" read the location, otherwise write the 
location.
          if (*argv[3] == 'r') {
              temp = pool[indx];
          }
          else {
              pool[indx] = temp;
          }
      }
      std::cout << "Success!\n";
  }

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2034607/+subscriptions


-- 
Mailing list: https://launchpad.net/~kernel-packages
Post to     : kernel-packages@lists.launchpad.net
Unsubscribe : https://launchpad.net/~kernel-packages
More help   : https://help.launchpad.net/ListHelp

Reply via email to