Bug 400610

Summary: Qt Applications crash with SIGBUS if XDG_CACHE_HOME directory is too small (or maybe out of space)
Product: [Frameworks and Libraries] frameworks-kcoreaddons Reporter: Mike <bugs>
Component: generalAssignee: Michael Pyne <mpyne>
Status: RESOLVED FIXED    
Severity: normal CC: alexey.min, kdelibs-bugs
Priority: NOR    
Version: 5.51.0   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:

Description Mike 2018-11-03 04:05:46 UTC
Qt application crashes with SIGBUS if XDG_CACHE_HOME is too small

We were able to track down the problem to:
- Package: kde-frameworks/kcoreaddons-5.51.0
- File: src/lib/caching/kshareddatacache.cpp
- Function: void mapSharedMemory()
- Lines: 1051 - 1053
- Code:	
  if (file.open(QIODevice::ReadWrite) &&
    (file.size() >= size ||
      (file.resize(size) && ensureFileAllocated(file.handle(), size)))) {

STEPS TO REPRODUCE
1) Create a small filesystem (as root): mount -t tmpfs -o size=30k tmpfs /mnt

The size seems to depend on the system. If the value in '-o size=' is too small or too high, you won't get a SIGBUS crash. 30k works on my machine.

2) Create a small test program (as suggested by user "mv")

File a.cc:
  #include <QApplication>
  #include <qframe.h>

  int main(int argc, char ** argv) {
   QApplication a(argc,argv);
   QFrame *view = new QFrame();
   view->show();
   a.connect(&a,SIGNAL(lastWindowClosed()),&a,SLOT(quit()));
   return a.exec();
  }	

File: a.pro	
  TARGET = a
  SOURCES = a.cc
  QT += widgets	

3) Compile the program and run it:

  qmake && make
  XDG_CACHE_HOME=/mnt ./a

4) The program will crash with SIGBUS

5) You can get more details using strace:

  XDG_CACHE_HOME=/mnt strace -o /tmp/strace-log -f ./a

6) strace-log shows:

  1500 openat(AT_FDCWD, "/mnt/icon-cache.kcache", O_RDWR|O_CREAT|O_CLOEXEC, 0666) = 9
  1500 statx(9, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS, stx_attributes=0, stx_mode=S_IFREG|0640, stx_size=0, ...}) = 0
  1500 lseek(9, 0, SEEK_CUR)             = 0
  1500 ftruncate(9, 10547304)            = 0
  1500 fallocate(9, 0, 0, 10547304)      = -1 ENOSPC (No space left on device)
  1500 mmap(NULL, 10547304, PROT_READ|PROT_WRITE, MAP_SHARED, 9, 0) = 0x7f71484df000   
  1500  --- SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRERR, si_addr=0x7f71484e3020} --- 

The mmap should have stopped with an error message after fallocate, since this function failed. But the error was ignored and the program continued with an mmap. A subsequent memory access to the mmaped memory failed with SIGBUS.  

7) The lines in the strace log correspond to lines 1051 - 1053 of src/lib/caching/kshareddatacache.cpp

OBSERVED RESULT: SIGBUS

EXPECTED RESULT: Error message stating that XDG_CACHE_HOME is out of space, followed by a regular abort() or exit().

SOFTWARE VERSIONS
- OS: Gentoo Linux
- KDE Plasma Version: 5.14.2
- KDE Frameworks Version: 5.51.0
- Qt Version: 5.11.2

ADDITIONAL INFORMATION
- Look at https://forums.gentoo.org/viewtopic-t-1087812.html for additional information.
Comment 1 Alexey Min 2018-11-07 20:41:35 UTC
I can reproduce this in Neon dev-unstable 18.04 (crashes every second run, or prints "Unable to acquire shared lock, is the cache corrupt?").
Comment 2 Alexey Min 2018-11-07 21:27:51 UTC
Proposed fix: https://phabricator.kde.org/D16744
Comment 3 Alexey Min 2018-11-07 23:33:48 UTC
Git commit eb916c305a5cd8683e7e8f955740a7c810220e19 by Alexey Min.
Committed on 07/11/2018 at 23:33.
Pushed by alexeymin into branch 'master'.

Fix crash if XDG_CACHE_HOME directory is too small or out of space

Summary:
Incorrect checking for error return code of posix_fallocate() causes function to think that everything is OK, while it is not, causing crash in some cases.
Related: bug 339829

Test Plan:
good test plan provided in https://bugs.kde.org/show_bug.cgi?id=400610 . Works like a charm, tested in KDE Neon dev-ustable

The reason for bug was that return value of posix_fallocate() was assumed to be negative on error, but in fact it is a positive integer. The check was `< 0`, whi should be `!= 0`. ( http://man7.org/linux/man-pages/man3/posix_fallocate.3.html )

With this fix applied test application does not crash, and the output in console widow is:
```
No space left on device. Check filesystem free space at your XDG_CACHE_HOME!
The operating system is unable to promise 10547304 bytes for mapped cache, abandoning the cache for crash-safety.
org.kde.kcoreaddons: Failed to establish shared memory mapping, will fallback to private memory -- memory usage will increase
```

Reviewers: dfaure, #frameworks, mpyne

Reviewed By: dfaure

Subscribers: cfeck, kde-frameworks-devel

Tags: #frameworks

Differential Revision: https://phabricator.kde.org/D16744

M  +4    -1    src/lib/caching/kshareddatacache_p.h

https://commits.kde.org/kcoreaddons/eb916c305a5cd8683e7e8f955740a7c810220e19
Comment 4 Mike 2018-11-07 23:47:57 UTC
@Alexey Min: thanks for fixing this bug so quickly! :-)
Comment 5 Mike 2018-11-08 23:28:50 UTC
For the sake of completeness: I found a very similar problem in Qt (same test program, missing test before mmap()): https://bugreports.qt.io/browse/QTQAINFRA-2381