linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* SIGBUS with mapped file on full device
@ 2023-09-25 17:19 Arno Renevier
  2023-10-07  0:56 ` Zygo Blaxell
  0 siblings, 1 reply; 2+ messages in thread
From: Arno Renevier @ 2023-09-25 17:19 UTC (permalink / raw)
  To: linux-btrfs

[-- Attachment #1: Type: text/plain, Size: 1846 bytes --]

Hi,

I noticed that when storing user data on btrfs, chromium is systematically
crashing with a SIGBUS signal when the disk is full. It does not happen
with ext4.

This seems to happen because after failing to write to a file
(unsuccessfully), chromium tries to write into a mapped file. That mapped
file has been previously opened, and fallocated, and some data has been
written into it to make sure the extent of the file is realized.

I will attach the reproduce the crash. It creates a file, fills it with
data, and opens memory maps that file. Then, it creates another file, and
tries to write into it until the disk runs  out of space. And then, it
tries to write into the mapped file. It always results in a SIBGUS crash
for me. There is no such crash with ext4.

Is it a bug in btrfs, or is the testcase (and chromium) doing
something improper? (and if so, what should be done instead?)



bug I opened against chromium:
https://bugs.chromium.org/p/chromium/issues/detail?id=1484662
chromium code executed before a file in mmaped:
https://source.chromium.org/chromium/chromium/src/+/main:base/files/file_util_posix.cc;l=967?q=file_util%20posix&ss=chromium

instructions how to run the testcase:

$ truncate block.img -s 200M
$ mkfs.btrfs -f block.img
$ mkdir -p mount_point
$ sudo mount -o loop block.img mount_point
$ sudo chown $USER:$USER -R mount_point
$ clang++ crash.cpp -o crash && rm -f mount_point/map.data
mount_point/file.data && ./crash mount_point

...


$ uname -a
Linux archlinux 6.5.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 13 Sep 2023
08:37:40 +0000 x86_64 GNU/Linux
$ btrfs --version
btrfs-progs v6.5.1
$ btrfs fi show

$ btrfs fi df mount_point
Data, single: total=8.00MiB, used=0.00B
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=32.00MiB, used=128.00KiB
GlobalReserve, single: total=5.50MiB, used=0.00B

[-- Attachment #2: crash.cpp --]
[-- Type: text/x-c++src, Size: 2480 bytes --]

#include <string>
#include <cstring>

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

bool is_directory(const char *path) {
  struct stat st;
  if (stat(path, &st) == -1) {
    printf("Error: %s\n", strerror(errno));
    return false;
  }
  return S_ISDIR(st.st_mode);
}

bool append(int fd, size_t size) {
  void* buffer = malloc(size);
  memset(buffer, 0, size);
  if (write(fd, buffer, size) <= 0) {
      printf("Error in append: %s\n", strerror(errno));
      free(buffer);
      return 0;
  }
  free(buffer);
  return 1;
}

int fd_open_file_for_create(const char *path) {
  int fd = open(path, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
  if (fd < 0) {
      printf("Error in file opening: %s\n", strerror(errno));
      return 1;
  }
  return fd;
}

int main(int argc, char **argv) {
  if (argc < 2) {
    printf("Usage: %s <directory>\n", argv[0]);
    return 1;
  }
  const char *directory = argv[1];
  if (!is_directory(directory)) {
    printf("Error: %s is not a directory\n", directory);
    return 1;
  }

  const std::string mapped_file_path = std::string(directory) + "/map.data";

  size_t mmap_size = 1024 * 1024 * 100;
  // create a large file filled with 0
  {
    int fd = fd_open_file_for_create(mapped_file_path.c_str());
    for (int i = 0; i < (mmap_size / 1024); i++) {
      if (!append(fd, 1024)) {
        break;
      }
    }
    close(fd);
  }

  // mmap that file
  int mapped_fd = open(mapped_file_path.c_str(), O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
  if (mapped_fd < 0) {
    printf("Error in file opening: %s\n", strerror(errno));
    return 1;
  }

  // we get the same behavior if we fallocate the mapped file, instead of filling it with zeros.
//  if (fallocate(mapped_fd, 0, 0, mmap_size) < 0) {
//    printf("Error in file allocation: %s\n", strerror(errno));
 //   return 1;
  //}

  char* ptr = (char*)mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, mapped_fd, 0);

  // open another file, and write into it until the disk is full
  const std::string data_file_path = std::string(directory) + "/file.data";
  // create a large file filled with 0
  {
    int fd = fd_open_file_for_create(data_file_path.c_str());
    for (int i = 0; i < (mmap_size / 1024); i++) {
      if (!append(fd, 1024)) {
        break;
      }
    }
    close(fd);
  }

  // now, try to write into the mapped file. With btrfs, it crashes. With ext4, it does not
  ptr[0] = 0;

  close(mapped_fd);
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-10-07  1:04 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-25 17:19 SIGBUS with mapped file on full device Arno Renevier
2023-10-07  0:56 ` Zygo Blaxell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).