Syllo / nvtop

GPU & Accelerator process monitoring for AMD, Apple, Huawei, Intel, NVIDIA and Qualcomm
Other
7.95k stars 291 forks source link

Failed to build from source because undefined reference to `reallocarray' #145

Closed AmosChenYQ closed 2 years ago

AmosChenYQ commented 2 years ago

Distributor ID: Ubuntu Description: Ubuntu 16.04.7 LTS Release: 16.04 Codename: xenial

I tried to build nvtop from source but got these error messages

CMakeFiles/nvtop.dir/extract_gpuinfo_amdgpu.c.o: In function `gpuinfo_amdgpu_get_running_processes':
extract_gpuinfo_amdgpu.c:(.text+0x1232): undefined reference to `reallocarray'
extract_gpuinfo_amdgpu.c:(.text+0x12d8): undefined reference to `reallocarray'
collect2: error: ld returned 1 exit status
src/CMakeFiles/nvtop.dir/build.make:290: recipe for target 'src/nvtop' failed
make[2]: *** [src/nvtop] Error 1
CMakeFiles/Makefile2:124: recipe for target 'src/CMakeFiles/nvtop.dir/all' failed
make[1]: *** [src/CMakeFiles/nvtop.dir/all] Error 2
Makefile:135: recipe for target 'all' failed
make: *** [all] Error 2

I searched for this reallocarray function, it is just a Standard C Library(libc, -lc) I don't think it can be missing. So I think it is because of lib link problem

Here below is the makefile generated by cmake:

# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.23

# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target

# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:

#=============================================================================
# Special targets provided by cmake.

# Disable implicit rules so canonical targets will work.
.SUFFIXES:

# Disable VCS-based implicit rules.
% : %,v

# Disable VCS-based implicit rules.
% : RCS/%

# Disable VCS-based implicit rules.
% : RCS/%,v

# Disable VCS-based implicit rules.
% : SCCS/s.%

# Disable VCS-based implicit rules.
% : s.%

.SUFFIXES: .hpux_make_needs_suffix_list

# Command-line flag to silence nested $(MAKE).
$(VERBOSE)MAKESILENT = -s

#Suppress display of executed commands.
$(VERBOSE).SILENT:

# A target that is always out of date.
cmake_force:
.PHONY : cmake_force

#=============================================================================
# Set environment variables for the build.

# The shell in which to execute make rules.
SHELL = /bin/sh

# The CMake executable.
CMAKE_COMMAND = /opt/cmake/bin/cmake

# The command to remove a file.
RM = /opt/cmake/bin/cmake -E rm -f

# Escaping for special characters.
EQUALS = =

# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/amoschenyq/nvtop

# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/amoschenyq/nvtop/build

#=============================================================================
# Targets provided globally by CMake.

# Special rule for the target edit_cache
edit_cache:
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..."
    /opt/cmake/bin/ccmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : edit_cache

# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast

# Special rule for the target rebuild_cache
rebuild_cache:
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
    /opt/cmake/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache

# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast

# Special rule for the target list_install_components
list_install_components:
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
.PHONY : list_install_components

# Special rule for the target list_install_components
list_install_components/fast: list_install_components
.PHONY : list_install_components/fast

# Special rule for the target install
install: preinstall
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
    /opt/cmake/bin/cmake -P cmake_install.cmake
.PHONY : install

# Special rule for the target install
install/fast: preinstall/fast
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
    /opt/cmake/bin/cmake -P cmake_install.cmake
.PHONY : install/fast

# Special rule for the target install/local
install/local: preinstall
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
    /opt/cmake/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local

# Special rule for the target install/local
install/local/fast: preinstall/fast
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
    /opt/cmake/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local/fast

# Special rule for the target install/strip
install/strip: preinstall
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
    /opt/cmake/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip

# Special rule for the target install/strip
install/strip/fast: preinstall/fast
    @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
    /opt/cmake/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip/fast

# The main all target
all: cmake_check_build_system
    $(CMAKE_COMMAND) -E cmake_progress_start /home/amoschenyq/nvtop/build/CMakeFiles /home/amoschenyq/nvtop/build//CMakeFiles/progress.marks
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 all
    $(CMAKE_COMMAND) -E cmake_progress_start /home/amoschenyq/nvtop/build/CMakeFiles 0
.PHONY : all

# The main clean target
clean:
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 clean
.PHONY : clean

# The main clean target
clean/fast: clean
.PHONY : clean/fast

# Prepare targets for installation.
preinstall: all
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall

# Prepare targets for installation.
preinstall/fast:
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall/fast

# clear depends
depend:
    $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend

#=============================================================================
# Target rules for targets named uninstall

# Build rule for target.
uninstall: cmake_check_build_system
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 uninstall
.PHONY : uninstall

# fast build rule for target.
uninstall/fast:
    $(MAKE) $(MAKESILENT) -f CMakeFiles/uninstall.dir/build.make CMakeFiles/uninstall.dir/build
.PHONY : uninstall/fast

#=============================================================================
# Target rules for targets named nvtop

# Build rule for target.
nvtop: cmake_check_build_system
    $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 nvtop
.PHONY : nvtop

# fast build rule for target.
nvtop/fast:
    $(MAKE) $(MAKESILENT) -f src/CMakeFiles/nvtop.dir/build.make src/CMakeFiles/nvtop.dir/build
.PHONY : nvtop/fast

# Help Target
help:
    @echo "The following are some of the valid targets for this Makefile:"
    @echo "... all (the default if no target is provided)"
    @echo "... clean"
    @echo "... depend"
    @echo "... edit_cache"
    @echo "... install"
    @echo "... install/local"
    @echo "... install/strip"
    @echo "... list_install_components"
    @echo "... rebuild_cache"
    @echo "... uninstall"
    @echo "... nvtop"
.PHONY : help

#=============================================================================
# Special targets to cleanup operation of make.

# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
    $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system
zhuyifei1999 commented 2 years ago

reallocarray is implemented in glibc 2.26 (https://github.com/bminor/glibc/commit/2e0bbbfbf95fc9e22692e93658a6fbdd2d4554da), but Ubuntu Xenial uses 2.23 (https://launchpad.net/ubuntu/xenial/+source/glibc)

We could polyfill this with gnulib (https://www.gnu.org/software/gnulib/MODULES.html#module=reallocarray) or something, but is it really worth the effort to support an already EOL-ed system?

AmosChenYQ commented 2 years ago

Thanks for replying, I have built and installed nvtop from an older version without the need of reallocarray. For the polyfill method, I will have a try.

Syllo commented 2 years ago

Indeed, reallocarray came from BSD and has been introduced "recently" in the glibc and even more recently in other libc (e.g. 2021 for musl libc). I'll add a check with CMake to see if the function exists, and otherwise use realloc. This should fix your compilation issue.

Syllo commented 2 years ago

@AmosChenYQ Could you please try if 816c34894b194d4ff5c1bcb2a6d3f366c1a78934 works for you? To do so,

cd nvtop
git fetch origin
git checkout reallocarrayFix

and follow the build process in the README

zhuyifei1999 commented 2 years ago
 #define reallocarray(ptr, nbmem, size) realloc(ptr, (nbmem) * (size))

The difference between a reallocarray and a realloc with a multiplication is that reallocarray will refuse to allocate when the multiplication overflows, which, if it happens, can lead to memory corruptions and potentially vulnerabilities.

My calculation shows that for our use cases, this isn't really exploitable. It would most likely overflow on a 32 bit arch, and we use it on two locations:

        seen_fds = reallocarray(seen_fds, seen_fds_capacity, sizeof(*seen_fds));

and

          *processes_info = reallocarray(*processes_info, processes_info_capacity, sizeof(**processes_info));

For seen_fds sizeof(*seen_fds) = 4, so the capacity would be at least (1<<32)/4 = 1073741824. The capacity increment are 1 below each power of two:

>>> 0
0
>>> _*2 + 1
1
>>> _*2 + 1
3
>>> _*2 + 1
7
>>> _*2 + 1
15
>>> _*2 + 1
31

So one would need at least 1073741824 fds on a single process pointing to different in-kernel struct file. That is impossible on 32 bit arch with the memory limitations.

For the latter case, I think sizeof(**processes_info) would be 64 (didn't compile, just doing this on paper):

/* offset      |    size */  type = struct gpu_process {
/*      0      |       4 */    enum gpu_process_type type;
/*      4      |       4 */    pid_t pid;
/*      8      |       4 */    char *cmdline;
/*     12      |       4 */    char *user_name;
/*     16      |       4 */    unsigned int gpu_usage;
/*     20      |       4 */    unsigned int encode_usage;
/*     24      |       4 */    unsigned int decode_usage;
/* XXX  4-byte hole      */
/*     32      |       8 */    unsigned long long gpu_memory_usage;
/*     40      |       4 */    unsigned int gpu_memory_percentage;
/*     44      |       4 */    unsigned int cpu_usage;
/*     48      |       4 */    unsigned long cpu_memory_virt;
/*     52      |       4 */    unsigned long cpu_memory_res;
/*     56      |       2 */    unsigned char valid[2];
/* XXX  6-byte padding   */

                               /* total size (bytes):   64 */
                             }

So to overflow this you need (1<<32)/64 = 67108864 PIDs (assuming the worst case scenario where an adversary is racing so that PIDs don't have to coexist at the same time). This is currently impossible but may possibly extendable in the future (per threads.h max PID is 32768 on 32bit, 4 million on 64 bit, but theoretical limit is 1 billion).

Do you think it would make sense to say, add a comment there stating that it's not currently exploitable but must be careful with any future use of reallocarray this way?

Syllo commented 2 years ago

@zhuyifei1999 Thanks for the analysis. And this is without saying that the OS will have to handle that many processes in the first place! I took a different approach and pushed a different patch, re-implementing the function altogether.

Syllo commented 2 years ago

Solution merged in master

AmosChenYQ commented 2 years ago

@AmosChenYQ Could you please try if 816c348 works for you? To do so,

cd nvtop
git fetch origin
git checkout reallocarrayFix

and follow the build process in the README

Sorry for the late response, I have replaced and built it successfully which I saw you have merged into main branch.