A pass to make "Permission denied" (errno: EACCES) detailed.
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:
flag & MASK1
flag & MASK2
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.
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
.
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
scripts/update_kernel.sh
to build & install the kernel.scripts/update_partial.sh <filename>
scripts/update_example.sh <filename>
scripts/emit_ll_example.sh <filename>
We use Coccinelle to replace ternary operator to if-else.
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.
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
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 ..
When you use wrong version of clang, the pass may not work. Use clang 17.
Solution:
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 ..
Used Adrian Sampson's LLLVM turorial as template. (Blog)