martinus / nanobench

Simple, fast, accurate single-header microbenchmarking functionality for C++11/14/17/20
https://nanobench.ankerl.com
MIT License
1.43k stars 82 forks source link

Failed to build with musl libc due to integer overflow in the argument for ioctl #92

Open erinacio opened 1 year ago

erinacio commented 1 year ago

This issue is found when trying to build nanobench latest bench and master commit on Alpine Linux (docker pull alpine:3.17) and a homemade musl toolchain on Arch Linux.

On Alpine Linux, a simple cmake <nanobench-dir> && make will show:

In file included from /tmp/nanobench/src/test/app/nanobench.cpp:2:
/tmp/nanobench/src/include/nanobench.h: In member function 'bool ankerl::nanobench::detail::LinuxPerformanceCounters::monitor(uint32_t, uint64_t, Target)':
/tmp/nanobench/src/include/nanobench.h:2679:25: error: overflow in conversion from 'long unsigned int' to 'int' changes value from '2148017159' to '-2146950137' [-Werror=overflow]
 2679 |     if (-1 == ioctl(fd, PERF_EVENT_IOC_ID, &id)) {
      |                         ^~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
make[2]: *** [CMakeFiles/nb.dir/build.make:76: CMakeFiles/nb.dir/src/test/app/nanobench.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:186: CMakeFiles/nb.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

Clang has similar error message:

In file included from /tmp/nanobench/src/test/app/nanobench.cpp:2:
/tmp/nanobench/src/include/nanobench.h:2679:25: error: implicit conversion changes signedness: 'unsigned long' to 'int' [-Werror,-Wsign-conversion]
    if (-1 == ioctl(fd, PERF_EVENT_IOC_ID, &id)) {
              ~~~~~     ^~~~~~~~~~~~~~~~~
/usr/include/linux/perf_event.h:507:29: note: expanded from macro 'PERF_EVENT_IOC_ID'
#define PERF_EVENT_IOC_ID                       _IOR('$', 7, __u64 *)
                                                ^~~~~~~~~~~~~~~~~~~~~
/usr/include/bits/ioctl.h:8:21: note: expanded from macro '_IOR'
#define _IOR(a,b,c) _IOC(_IOC_READ,(a),(b),sizeof(c))
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/bits/ioctl.h:1:52: note: expanded from macro '_IOC'
#define _IOC(a,b,c,d) ( ((a)<<30) | ((b)<<8) | (c) | ((d)<<16) )
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
1 error generated.
make[2]: *** [CMakeFiles/nb.dir/build.make:76: CMakeFiles/nb.dir/src/test/app/nanobench.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:186: CMakeFiles/nb.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

After some investigation, turns out that this issue may be caused by glibc and musl using different signatures for ioctl. In glibc, ioctl is defined like (copied from Arch Linux /usr/include/sys/ioctl.h):

extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

while in musl libc:

int ioctl (int, int, ...);

PERF_EVENT_IOC_ID is evaluated to 2148017159 and thus overflows int, triggering -Woverflow.

Some Alpine Linux users already found this problem and reported it to musl. However, seems that musl maintainers were not willing to change it as that's exactly what POSIX specified.