nanana37 / eacces-modifier

Make "Permission denied" detailed.
0 stars 0 forks source link

eacces-modifier

A pass to make "Permission denied" (errno: EACCES) detailed.

What is this?

We can't identify the reason between Error1 and Error2 in the following code.

if ( flag & MASK1 ) {
  return -EACCES;  // Error1
}
if ( flag & MASK2 ) {
  return -EACCES;  // Error2
}

Because they are both EACCES, and say "Permission denied".

$ ./a.out
Permision denied

This tool will make logs about:

Build

Environment (Prerequisites?)

Build command

This makes a shared object build/permod/PermodPass.{so|dylib} (.so on Linux / .dylib on macOS).

cd eacces-modifier
mkdir build
cd build
cmake ..  # Generate the Makefile.
make  # Actually build the pass.

See Trouble shooting if you encounter a CMake error.

Usage

Apply to the entire kernel

To apply the pass to the kernel, you need to clone torvalds/linux and prepare to build. This site explains well how to build.

You can change the -j option. If -j 1, the compile-time log will be output sequentially.

# In the build dir of Linux kernel
make -j 8 \
  CC=clang \
  KCFLAGS="-fno-discard-value-names \
  -fpass-plugin=path_to_build/permod/PermodPass.so"

The logs will be in /var/log/kern.log, because we use printk.

Apply to a specific file

The pass can be applied to both a spcific file, a piece of Linux, and your original test file.

Simple example:

A piece of Linux

# In the build dir of Linux kernel
make CC=clang \
  KCFLAGS="-fno-discard-value-names \
  -fpass-plugin=path_to_build/permod/PermodPass.so" \
  fs/namei.o

Your original file

clang -fpass-plugin=path_to_build/permod/PermodPass.so something.c

Using Script

  1. Apply to the entire kernel
  1. Apply to a piece of Linux
  1. Apply to your original file
  1. Emit IR for your original file

Coccinelle

We use Coccinelle to replace ternary operator to if-else.

Why?

Linux Kernel uses ternary operator to set return value.

For example:

return acl ? -EACCES; 0;

This C code is translated to select instruction on LLVM IR.

bb:
  %tobool = cmp ne %acl, 0
  %retval = select %tobool, -13, 0
  br label %return

return:
  ret %retval

Permod overlooks the condition of the select, because select includes a branch inside itself.

Usage

Create patch at linux repository.

make coccicheck MODE=patch COCCI=path_to_permod/scripts/coccinelle/sel_to_br.cocci > path_to_patch

Apply patch.

patch -p1 < path_to_patch

Trouble shooting

CMake error

When building with CMake, you may encounter the following error:

CMake Error at CMakeLists.txt:13 (include):
  include could not find load file:

    AddLLVM

CMake Error at permod/CMakeLists.txt:1 (add_llvm_pass_plugin):
  Unknown CMake command "add_llvm_pass_plugin".

-- Configuring incomplete, errors occurred!
See also "/home/hiron/eacces-modifier/bui/CMakeFiles/CMakeOutput.log".

This is because the LLVM is not installed globally. You need to give CMake the path to the share/llvm/cmake. Do this after cleaning the build dir.

e.g.,

LLVM_DIR=/usr/lib/llvm-17/lib/cmake/llvm cmake ..

# For Homebrew
LLVM_DIR=`brew --prefix llvm@17`/lib/cmake/llvm cmake ..

No output

When you use wrong version of clang, the pass may not work. Use clang 17.

Solution:

LSP 'clangd' cannot find the header files

Clangd cannot follow third-party header files. Set CMAKE_EXPORT_COMPILE_COMMANDS to 1 for CMake command.

e.g.,

CMAKE_EXPORT_COMPILE_COMMANDS=1 cmake ..

References

Used Adrian Sampson's LLLVM turorial as template. (Blog)