linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Arno Renevier <arno@switchboard.app>
To: linux-btrfs@vger.kernel.org
Subject: SIGBUS with mapped file on full device
Date: Mon, 25 Sep 2023 10:19:44 -0700	[thread overview]
Message-ID: <CAFmLMRMYzSfmJvp0gccr0siT6jX8Nv5Xt9jVKfiUeXhpy3WRqw@mail.gmail.com> (raw)

[-- 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);
}

             reply	other threads:[~2023-09-25 17:20 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-25 17:19 Arno Renevier [this message]
2023-10-07  0:56 ` SIGBUS with mapped file on full device Zygo Blaxell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAFmLMRMYzSfmJvp0gccr0siT6jX8Nv5Xt9jVKfiUeXhpy3WRqw@mail.gmail.com \
    --to=arno@switchboard.app \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).