Project: liblouis (liblouis/liblouis), language: c++
Vulnerability instance: liblouis.cve-2023-26768
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/liblouis/liblouis/issues/1301
## Description:
Issue: liblouis/liblouis#1301
Title: global-buffer-overflow in lou_logFile() when long filename is given
State: closed
Created by: Marsman1996
Created at: 2023-02-09 03:17:32+00:00
Labels: bug, memory error

Issue Body:
## Summary
When long filename (larger than 256) is given to API `lou_logFile()`, there will be a global-buffer-overflow.

https://github.com/liblouis/liblouis/blob/517f6f1f35fc5c57ac2f045caa168191bdeaadde/liblouis/logging.c#L121-L130

## Test Environment
Ubuntu 16.04.3 LTS  
liblouis (master, 6223f21)

## How to trigger
1. Compile liblouis with AddressSanitizer
2. Compile the [fuzz driver](https://raw.githubusercontent.com/Marsman1996/pocs/master/liblouis/driver-API-6223f21-lou_logFile-BO.c) and [poc file](https://raw.githubusercontent.com/Marsman1996/pocs/master/liblouis/poc-API-6223f21-lou_logFile-BO)
3. Compile the fuzz driver: `$ clang -g -fsanitize=address,fuzzer ./driver-API-6223f21-lou_logFile-BO.c ./bin_asan/lib/liblouis.a -I ./bin_asan/include/liblouis/ -o driver-API-6223f21-lou_logFile-BO`
4. run the compiled driver: `$ ./driver-API-6223f21-lou_logFile-BO poc-API-6223f21-lou_logFile-BO`

## ASAN report
```
$ ./driver-API-6223f21-lou_logFile-BO poc-API-6223f21-lou_logFile-BO
Minimum size is 0
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2700932249
INFO: Loaded 1 modules   (57 inline 8-bit counters): 57 [0x7b0360, 0x7b0399), 
INFO: Loaded 1 PC tables (57 PCs): 57 [0x5705c0,0x570950), 
./driver-API-6223f21-lou_logFile-BO: Running 1 inputs 1 time(s) each.
Running: poc-API-6223f21-lou_logFile-BO
=================================================================
==20412==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00000108b6e0 at pc 0x00000050dbf8 bp 0x7ffe6a0ffac0 sp 0x7ffe6a0ff280
WRITE of size 4098 at 0x00000108b6e0 thread T0
    #0 0x50dbf7 in strcpy /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:423:5
    #1 0x55492e in lou_logFile /opt/disk/marsman/liblouis/6223f21/build_asan/liblouis/../../code/liblouis/logging.c:130:34
    #2 0x553634 in AFG_func /opt/disk/marsman/liblouis/6223f21/./driver-API-6223f21-lou_logFile-BO.c:17:2
    #3 0x553850 in LLVMFuzzerTestOneInput /opt/disk/marsman/liblouis/6223f21/./driver-API-6223f21-lou_logFile-BO.c:44:2
    #4 0x459911 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:599:15
    #5 0x4435d2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:323:6
    #6 0x449940 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:856:9
    #7 0x4738c2 in main /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #8 0x7f8ea559983f in __libc_start_main /build/glibc-S7Ft5T/glibc-2.23/csu/../csu/libc-start.c:291
    #9 0x41e0d8 in _start (/opt/disk/marsman/liblouis/6223f21/driver-API-6223f21-lou_logFile-BO+0x41e0d8)

0x00000108b6e0 is located 0 bytes to the right of global variable 'initialLogFileName' defined in '../../code/liblouis/logging.c:121:13' (0x108b5e0) of size 256
SUMMARY: AddressSanitizer: global-buffer-overflow /local/mnt/workspace/bcain_clang_vm-bcain-aus_3184/final/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:423:5 in strcpy
Shadow bytes around the buggy address:
  0x000080209680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9
  0x000080209690: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
  0x0000802096a0: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
  0x0000802096b0: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x0000802096c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000802096d0: 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9 f9 f9
  0x0000802096e0: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000802096f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080209700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080209710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080209720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==20412==ABORTING
```


Comments:
Comment by Marsman1996 on 2023-02-09 03:35:38+00:00:
I checked other `strcpy()` functions in this library, they are OK since the size of the src and the dest buffer is equal.

Except for the `strcpy()` in `print_dots()` at lou_trace.c:
https://github.com/liblouis/liblouis/blob/018edce262d65d99bf80ce7af5a82c0b32a898e9/tools/lou_trace.c#L100-L105

The max size of the src buffer (i.e., `scratchBuf`) returned by `_lou_showDots()` is 2048.
https://github.com/liblouis/liblouis/blob/1cf768700e9b42e4bf655c72f041a03f30b865a6/liblouis/utils.c#L260-L275

While the size of the dest buffer (i.e., `dots`) is 512.

However, the above is my static analysis result, and for now I cannot trigger the overflow from the program or API entry.
So maybe it is a false positive.

---

Comment by Marsman1996 on 2023-02-09 03:37:21+00:00:
Should I commit the fix together with #1300, or open a new pull request?

---

Comment by egli on 2023-02-09 08:22:12+00:00:
> Should I commit the fix together with #1300, or open a new pull request?

Hi @Marsman1996 , please open a new PR for this issue, thanks

---

Comment by Marsman1996 on 2023-02-09 10:59:25+00:00:
Hi, I open a new PR #1302 to fix this problem.

Commit References:
018edce262d65d99bf80ce7af5a82c0b32a898e9
1cf768700e9b42e4bf655c72f041a03f30b865a6
517f6f1f35fc5c57ac2f045caa168191bdeaadde

Build script:
#!/bin/bash
# Default build script template for C/C++ projects
set -eu

# Define configuration options
CONFIG_OPTS="--disable-shared"
CMAKE_OPTS="-DCMAKE_BUILD_TYPE=Debug"
MAKE_OPTS="-j$(nproc)"
BUILD_DIR="build"

# 1. Run build preparation scripts if they exist
for prep_script in buildconf autogen.sh bootstrap; do
  if [ -x "$prep_script" ]; then
    echo "Running $prep_script..."
    ./$prep_script
    break
  elif [ -f "$prep_script" ]; then
    echo "Found $prep_script but it's not executable, setting permissions..."
    chmod +x $prep_script
    ./$prep_script
    break
  fi
done

# 2. Handle different build systems
if [ -f "configure" ]; then
  echo "Found configure script, running with options: $CONFIG_OPTS"
  ./configure $CONFIG_OPTS

  echo "Building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -f "CMakeLists.txt" ]; then
  echo "Found CMakeLists.txt, using CMake build system"
  mkdir -p $BUILD_DIR
  cd $BUILD_DIR
  cmake $CMAKE_OPTS ..
  make $MAKE_OPTS
elif [ -f "Makefile" ] || [ -f "makefile" ]; then
  echo "Found Makefile, building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -f "meson.build" ]; then
  echo "Found meson.build, using Meson build system"
  mkdir -p $BUILD_DIR
  meson setup $BUILD_DIR .
  cd $BUILD_DIR
  ninja
elif [ -f "GNUmakefile" ]; then
  echo "Found GNUmakefile, building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -d ".git" ] && [ -f "Makefile.am" ]; then
  echo "Found Makefile.am, running autoreconf"
  autoreconf -i
  ./configure $CONFIG_OPTS
  make $MAKE_OPTS
else
  echo "WARNING: No recognized build system found!"
  echo "Trying generic approach:"
  
  # Try to detect common source patterns and compile them
  if ls *.c >/dev/null 2>&1 || ls *.cpp >/dev/null 2>&1 || ls *.cc >/dev/null 2>&1; then
    echo "Found C/C++ source files, attempting direct compilation"
    if ls *.cpp >/dev/null 2>&1 || ls *.cc >/dev/null 2>&1; then
      CXX=${CXX:-g++}
      echo "Compiling C++ sources with $CXX"
      $CXX -g -O1 -fsanitize=address -fno-omit-frame-pointer -o main *.cpp *.cc 2>/dev/null || true
    elif ls *.c >/dev/null 2>&1; then
      CC=${CC:-gcc}
      echo "Compiling C sources with $CC"
      $CC -g -O1 -fsanitize=address -fno-omit-frame-pointer -o main *.c 2>/dev/null || true
    fi
  fi
fi

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool pkg-config zlib1g-dev libpci-dev
RUN git clone https://github.com/liblouis/liblouis liblouis
RUN git -C liblouis checkout f628d16ab5fb82160e92b07fe4d9ed96fbab325e
WORKDIR $SRC/liblouis
COPY build.sh $SRC/

Additional files: