AMPLE: Fine-grained File Access Policies for Server Applications
C++
0
1 commits
updated Nov 17, 2025
[!NOTE] We are publishing this tool as open source solely for the purpose of reproducibility of the data in our paper. We will not be providing ongoing maintenance or support for this project.
AMPLE builds AppArmor MAC policies for server applications by combining static and dynamic analysis. AppArmor policies can restrict a process from accessing irrelavent (and possibly security-critical) IPC resources. In our current version, AMPLE builds policies to restrict access to files.
To this end, AMPLE first analyzes a program statically, extracting any file paths available in the source code. It then identifies and instruments initialization instructions for those that are only available at runtime. Given that many of the files required by server applications are only specified at runtime (e.g., path to the configuration file specified through a commandline option), AMPLE uses these instrumentations to extract their value at runtime.
To summarize, AMPLE performs the following steps to analyze a program:
We have developed a script that takes care of invoking all the necessary scripts that perform the steps above (discussed further below).
We use an LLVM pass to analyze and instrument the program.
Our pass is built on top of SVF. It can be used both as a standalone program
or with the opt toolkit. However, to utilize AMPLE's runtime analysis, we must
instrument the program, which requires running it through the opt toolkit.
Furthermore, our analysis depends on an SVF pointer analysis to generate sound results. Although our implementation allows running AMPLE without enabling SVF, doing so makes the results unsound.
This is due to the following reasons:
NOTE If the provided script (runAll.sh) is used, all the required options will be
used when running our analysis, and the issues mentioned above will be addressed.
The LLVM pass generates an initial policy that requires bootstrapping to be converted to the final policy. We have created a Python script that takes the LLVM pass output as its input and combines it with runtime file paths extracted by running the instrumented binary to generate a final policy. This is the policy that is used to enforce AppArmor for the program.
If you choose to build our pass on your own system, the following prerequisites must be met:
sudo apt install python3 apparmor apparmor-utilsNOTE: We have used SVF v2.2 in our repository, which is compatible with LLVM v12. As of October 2025, the most recent SVF version is 3.1 which is compatible with LLVM v16. If you need to run AMPLE with a newer LLVM version, then you must modify both our AMPLE LLVM pass and SVF to make it compatible with the newer LLVM version.
To build binutils-2.42 from the source code, we must follow these steps:
wget https://ftp.gnu.org/gnu/binutils/binutils-2.42.tar.xz
tar -xvf binutils-2.42.tar.xz
mkdir binutils.build
../binutils-2.42/configure --enable-gold --enable-plugins --disable-werror
make all-gold
To build LLVM 12 from the source code with the Gold plugin support, we must follow these steps (taken from the SVF repository):
Then run the following commands:
tar xf llvm-12.0.0.src.tar.xz
tar xf clang-12.0.0.src.tar.xz
mv clang-12.0.0.src llvm-12.0.0.src/tools/clang
Create a build directory and then run the following:
mkdir llvm-12.0.0.obj
cd llvm-12.0.0.obj
cmake -DLLVM_BINUTILS_INCDIR=/huge/hamed/tools/binutils-2.42/include/ -DCMAKE_BUILD_TYPE=Release ../llvm-12.0.0.src (or add "-DCMAKE_BUILD_TYPE:STRING=Debug" for debug version)
make -j8
Create a setenv.sh script that sets paths for LLVM and SVF in the required environment variables:
export LLVM_SRC=your_path_to_llvm-12.0.0.src
export LLVM_OBJ=your_path_to_llvm-12.0.0.obj
export LLVM_DIR=your_path_to_llvm-12.0.0.obj
export PATH=$LLVM_DIR/bin:$PATH
export SVF_DIR=[AMPLE_ROOT]/SVF-2.2
Set the environment variables before proceeding.
chmod +x setenv.sh
source setenv.sh
To build SVF you need to create build directory, set up the build scripts
using cmake and then build using make.
# Under ample/SVF-2.2:
mkdir Release-build
cd Release-build
cmake .. # If no luck with default compiler, CC=clang CXX=clang++ cmake ..
make -j8
After building LLVM and SVF and adding the paths to the PATH environment variable, we can build the project by creating a build folder and using CMake.
Clone the repository
git clone https://github.com/bloomberg/ample.git
Build the LLVM pass
cd [AMPLE_ROOT]/src/c++/syscall-arg-analyzer;
mkdir build
cd build
CC=clang CXX=clang++ cmake ../ (add -DCMAKE_BUILD_TYPE:STRING=Debug for debug build)
Run the pass
./bin/ample
Please keep in mind that we need to use the LLVM-provided opt to run the analysis if we want to instrument the binary to extract runtime values. By running AMPLE itself, you
can only perform static analysis and identify how many values can be
(potentially) extracted at runtime. The runtime values themselves need to be
extracted by running the instrumented program.
The scripts which run the analysis rely on certain environment-specific variables to find the paths to the compiled AMPLE pass and other scripts they depend upon. The paths required by this script are the following:
export AMPLE_HOME="[path_to_ample]/ample/" # Specifies path to root of ample repo
export AMPLE_BUILD="[path_to_ample]/ample/src/c++/syscall-arg-analyzer/Debug-build.chain-overhaul" # Specifies path to build of ample
export SVF_DIR="[path_to_ample]/ample/SVF-2.2/" # Path to SVF src directory
export LLVM_DIR="[path_to_llvm]/llvm-12.0.0.obj/" # Path to LLVM build directory
export APIARG_DIR="$AMPLE_HOME/data/" # Path to folder which contains file with api-arg pairs we want to extract values for
These variable are set through a setenv.sh file located in
[AMPLE_ROOT]/src/c++/syscall-arg-analyzer. Assuming that the LLVM_DIR and
SVF_DIR have been set above you only need to change the AMPLE_HOME and
AMPLE_BUILD variables.
AMPLE_HOME: This variable should contain the path to the root AMPLE directory.
This is the main repository folder on your local system.
AMPLE_BUILD: This variable should contain the path to the local AMPLE build
directory.
Please modify these paths before proceeding to the next steps.
Our analysis takes the LLVM IR as its input. Therefore, to run our analysis on an application or library, you need to first generate its bitcode. We have provided a guide on how to do that.
AMPLE expects the developer to provide the transition point between the initialization and processing phases for its multi-phase analysis. Through its multi-phase analysis, AMPLE can determine whether a file path is initialized during the program startup or in its processing phase. To do so, you must annotate the source code; explanation here.
Also, to add instrumentations for extracting runtime-available file path values, we must link the target program's bitcode with our instrumentation functions. These functions are then invoked when an initialization instruction is identified. We have created a script for adding these instrumentations to each program:
cd [AMPLE_ROOT]/src/c++/syscall-arg-analyzer/instrumentation
clang -emit-llvm -c ample_instrument.c -o ample_instrument.bc
./instrument-all.sh [folder_with_original_bitcodes] ample_instrument.bc $AMPLE_HOME/bc.outputs [appname]
The [appname] in this command is optional and can be used to run the script for only one program.
NOTE: This script only adds the instrumentation functions to the program bitcode. We must run the LLVM pass to identify the instructions that must be instrumented and add function calls to those instrumentation functions.
We have created a runAll.sh script to run all the steps needed to generate
an AppArmor policy using AMPLE.
The runAll.sh script which is in the src/c++/syscall-arg-analyzer folder
takes care of all the options that need to be passed to AMPLE.
NOTE: After generating an instrumented LLVM bitcode, AMPLE compiles the bitcode into the final binary. For this step, we must have installed any libraries that the program depends upon so that it can be linked with the program. The dependencies for all the programs in our dataset are as follows:
sudo apt install libssl-dev libpcre2-dev libpcre2-8-0 libpam0g-dev libfastjson-dev uuid-dev libevent-dev libxml2-dev libcap-dev libdb-dev zlib1g-dev
NOTE: AMPLE needs to run the target application to extract the runtime-based
file paths. Therefore, each application must have a valid configuration file with
which it can be run. Valid configuration files have been provided for the applications
in our test set. However, in case the runtime setting of one of these applications
conflicts with another program running on the target system, it may lead to invalid
policy generation. It is critical to go through and check these issues before running
AMPLE for an application. For example, based on our provided configuration file for Nginx,
it will run on port 8080. Therefore, if another program is running on this port, you need
to modify Nginx's port before running AMPLE.
The configuration files for all applications can be found in [AMPLE_HOME]/app_run_env.
NOTE: Some programs have other environment-related properties in their
configuration file that need to be modified before being executed.
The settings have been listed below:
- Lighttpd: The lighttpd.conf file found in our app_run_env folder specifies
the user and group to run lighttpd with. You must change the user and group
defined there to a valid user and group on your system. Furthermore,
you need to change the same properties which are also used in the
setup_run_env.sh script of lighttpd. This file can be found in
[AMPLE_ROOT]/app_run_env/lighttpd.
To run the script we use the following command:
./runAll.sh [appname] instrument validate
Instead of [appname], you can either specify the application name for which you
would like to run the analysis or use 'all' to run it for all of the apps in
our dataset.
The instrument option is optional, it specifies whether an instrumented
bitcode should be generated or only the static analysis portion should be run.
If the instrument option is passed, you can also pass the validate option
to validate the generated policy by running the program.
Examples:
./runAll.sh memcached
This would run the analysis for memcached and not build an instrumented binary. The generated policy would only contains paths derived statically.
./runAll.sh memcached instrument
This would run the analysis for memcached, but also create an instrumented
binary. Even though the instrumented binary is generated, you stil need to pass
the validate option for the script to run the program and extract runtime-available
file paths as well.
./runAll.sh memcached instrument validate
This command would run all the steps and generate an AppArmor policy which contains both statically derived and dynamically derived policies.
The applications currently available (when using runAll.sh the names should have the same syntax as specified below):
By default, the following paths will be used to store the output of the analysis:
$AMPLE_HOME/logs: full log of running the analysis will be stored separately for each application in this folder (named by the app)$AMPLE_HOME/policies: the statically derived policy, and the final policy will be stored in two separate policy files in this folder$AMPLE_HOME/bc.outputs: if the instrument option is specified the instrumented bitcode will be stored in this path$AMPLE_HOME/bin.outputs: if the instrument option is specified the instrumented binary will be stored in this pathThe Python script and its respective files are located in [AMPLE_ROOT]/src/python.
To use the Python script, we first need to provide its settings through the
config.json file available in this folder.
These settings include general options that are the same for all programs across
the same OS, and application-specific options that are specified for each program
separately.
The general-purpose options include: enforce-cmd and disable-cmd which specify
how to enable and disable an AppArmor policy (this might change in different OS versions).
Another option is policy-folder which is the default path to store the generated
policies.
The next option is libc-required-libraries. The standard C library (glibc) can
dynamically load other libraries. Since we do not analyze glibc statically --- because
it cannot be compiled with LLVM --- we expect these libraries to be provided manually.
This should not change regularly. However, if it does, these library names or paths
should be specified here. If the library name pattern is provided, our script will
attempt to find a library file matching this name and add its full path. If the
path is provided, AMPLE will add that path as is to the final policy.
We also have application-specific options which are configured in the apps section
of the JSON file.
We have already provided the settings needed
for the 10 applications in our dataset. You must configure these settings when
adding a new program to the dataset. You can override some of these options
through the commandline when invoking the Python script.
Each application has six main properties which should be configured, however,
if you use the runAll.sh script to run AMPLE, it will override all the file
paths specified in this JSON file when invoking this Python script, so the correct
paths are passed to the script. Therefore, there is no need to modify these paths in
the JSON file.
"memcached": {
"llvm-policy-path": "[llvm-policy-path]",
"format" : "apparmor",
"binary-path": "/home/ssm-user/analysis/apps/memcached-1.6.9/memcached",
"runtime-paths-auto": "/tmp/ample/memcached.instrumented",
"runtime-paths-manual": {
},
"caps-manual": [
"setgid",
"setuid"
]
},
llvm-policy-path: path to the policy built by the LLVM pass --- no need to change
if the Python script is called through runAll.sh
format: for now we only support AppArmor policies, but it can be extended to
support SELinux
binary-path: path to the binary of the target application or library. This is
required to extract the libraries linked to it --- no need to change if the Python
script is called through runAll.sh
runtime-paths-auto: the path to the files where we store runtime-extracted file
path values --- no need to change if the Python script is called through runAll.sh
runtime-paths-manual: a dictionary with paths specified at runtime along with their
permissions.
caps-manual: AppArmor policies can restrict the capabilities used by a program as
well. Our current AMPLE design does not support automatically extracting program's
required capabilities, so we must add the program's needed capabilities here.
This script will be run when using the runAll.sh script.
runAll.sh ScriptThe runAll.sh script contains all the options and commands to run AMPLE for a
program. In this section we have provided details on this script and its parameters.
Since runAll.sh executes both the LLVM pass and the Python script, we have defined
the required command line options for each of these analyses in this script.
We will first describe the LLVM pass command line options that determine which
optional features of the pass should be executed. (To see a full list of all the
available runtime options for AMPLE's LLVM pass we can use:
[AMPLE_BUILD_DIR]/ample --help | grep AMPLE)
These options are defined in the following variable names:
AMPLEOPTS: The final options used to run the LLVM pass.
GENERALOPTS: These are the LLVM pass options that we are currently using when
analyzing any program. At some point, we could specify these values in the LLVM
pass source as the default values and refrain from repeating them here.
TYPEOPT: Our pass uses type-based argument filtering for reducing the spurious
indirect function call edges. This was used in the Temporal System Call Specialization
paper. We do not add this option in the general options (GENERALOPTS), because it
can lead to soundness issues for programs developed with C++.
INSTRUMENTOPT: This variable adds the -enable-instrument option which can only
be used if the pass is run through opt. It enables the instrumentation part of
the program.
PTSLOOP: This variable adds the -pts-based-depth option which is an experimental
feature that is not fully tested yet.
These are the options that are defined the same across all programs. We also have
program-specific options that are defined in the if clauses in the script.
The program-specific options are as follows:
AMPLEOPTS: This is the final variable used to run the LLVM pass. In the
program-specific options we add the transition point used for our multi-phase
analysis (more details).
LIBS: The output of our AMPLE pass is an instrumented bitcode that needs to
be compiled into the final binary before being launched. To compile the bitcode,
we also need to link it with the libraries it depends upon. This variable
contains a list of the libraries that must be linked with the program.
RUNOPTS: Generating the final policy requires executing the instrumented
binary to extract the runtime file path values. To do so, some programs must
be run with specific command line options. We specify these options through
this variable. Each runtime option must be added separately. For example,
we run need to run nginx as follows: nginx -g 'daemon off; master_process off;'
and add the options using the following:
RUNOPTS+=("-g")
RUNOPTS+=("daemon off; master_process off;")
SUDO: Some programs need root permissions to launch correctly (e.g., nginx
needs root permission if it runs on a port <1024). For these cases, we can
enable the SUDO variable so the program is executed with sudo.
We do not accept contributions on this project, as it has only been released to allow our paper's data to be reproduced.
Distributed under the <License name> License. See LICENSE for more
information.
Hamed Ghavamnia - @s_hamedgh - sghavamnia@bloomberg.net
Project Link: https://github.com/bloomberg/ample
Please consider citing our paper if you found our tool useful.
@inproceedings{amplease25,
year={2025},
booktitle={Proceedings of the 40th IEEE/ACM International Conference on Automated Software Engineering (ASE)},
title={AMPLE: Fine-grained File Access Policies for Server Applications},
author={Ghavamnia, Seyedhamed and Vanegue, Julien}
}
1 commits
C++
56.2%
Python
17.3%
HTML
13.7%
Makefile
6.5%
Shell
4.9%
AMPLE: Fine-grained File Access Policies for Server Applications
C++
0
1 commits
updated Nov 17, 2025
[!NOTE] We are publishing this tool as open source solely for the purpose of reproducibility of the data in our paper. We will not be providing ongoing maintenance or support for this project.
AMPLE builds AppArmor MAC policies for server applications by combining static and dynamic analysis. AppArmor policies can restrict a process from accessing irrelavent (and possibly security-critical) IPC resources. In our current version, AMPLE builds policies to restrict access to files.
To this end, AMPLE first analyzes a program statically, extracting any file paths available in the source code. It then identifies and instruments initialization instructions for those that are only available at runtime. Given that many of the files required by server applications are only specified at runtime (e.g., path to the configuration file specified through a commandline option), AMPLE uses these instrumentations to extract their value at runtime.
To summarize, AMPLE performs the following steps to analyze a program:
We have developed a script that takes care of invoking all the necessary scripts that perform the steps above (discussed further below).
We use an LLVM pass to analyze and instrument the program.
Our pass is built on top of SVF. It can be used both as a standalone program
or with the opt toolkit. However, to utilize AMPLE's runtime analysis, we must
instrument the program, which requires running it through the opt toolkit.
Furthermore, our analysis depends on an SVF pointer analysis to generate sound results. Although our implementation allows running AMPLE without enabling SVF, doing so makes the results unsound.
This is due to the following reasons:
NOTE If the provided script (runAll.sh) is used, all the required options will be
used when running our analysis, and the issues mentioned above will be addressed.
The LLVM pass generates an initial policy that requires bootstrapping to be converted to the final policy. We have created a Python script that takes the LLVM pass output as its input and combines it with runtime file paths extracted by running the instrumented binary to generate a final policy. This is the policy that is used to enforce AppArmor for the program.
If you choose to build our pass on your own system, the following prerequisites must be met:
sudo apt install python3 apparmor apparmor-utilsNOTE: We have used SVF v2.2 in our repository, which is compatible with LLVM v12. As of October 2025, the most recent SVF version is 3.1 which is compatible with LLVM v16. If you need to run AMPLE with a newer LLVM version, then you must modify both our AMPLE LLVM pass and SVF to make it compatible with the newer LLVM version.
To build binutils-2.42 from the source code, we must follow these steps:
wget https://ftp.gnu.org/gnu/binutils/binutils-2.42.tar.xz
tar -xvf binutils-2.42.tar.xz
mkdir binutils.build
../binutils-2.42/configure --enable-gold --enable-plugins --disable-werror
make all-gold
To build LLVM 12 from the source code with the Gold plugin support, we must follow these steps (taken from the SVF repository):
Then run the following commands:
tar xf llvm-12.0.0.src.tar.xz
tar xf clang-12.0.0.src.tar.xz
mv clang-12.0.0.src llvm-12.0.0.src/tools/clang
Create a build directory and then run the following:
mkdir llvm-12.0.0.obj
cd llvm-12.0.0.obj
cmake -DLLVM_BINUTILS_INCDIR=/huge/hamed/tools/binutils-2.42/include/ -DCMAKE_BUILD_TYPE=Release ../llvm-12.0.0.src (or add "-DCMAKE_BUILD_TYPE:STRING=Debug" for debug version)
make -j8
Create a setenv.sh script that sets paths for LLVM and SVF in the required environment variables:
export LLVM_SRC=your_path_to_llvm-12.0.0.src
export LLVM_OBJ=your_path_to_llvm-12.0.0.obj
export LLVM_DIR=your_path_to_llvm-12.0.0.obj
export PATH=$LLVM_DIR/bin:$PATH
export SVF_DIR=[AMPLE_ROOT]/SVF-2.2
Set the environment variables before proceeding.
chmod +x setenv.sh
source setenv.sh
To build SVF you need to create build directory, set up the build scripts
using cmake and then build using make.
# Under ample/SVF-2.2:
mkdir Release-build
cd Release-build
cmake .. # If no luck with default compiler, CC=clang CXX=clang++ cmake ..
make -j8
After building LLVM and SVF and adding the paths to the PATH environment variable, we can build the project by creating a build folder and using CMake.
Clone the repository
git clone https://github.com/bloomberg/ample.git
Build the LLVM pass
cd [AMPLE_ROOT]/src/c++/syscall-arg-analyzer;
mkdir build
cd build
CC=clang CXX=clang++ cmake ../ (add -DCMAKE_BUILD_TYPE:STRING=Debug for debug build)
Run the pass
./bin/ample
Please keep in mind that we need to use the LLVM-provided opt to run the analysis if we want to instrument the binary to extract runtime values. By running AMPLE itself, you
can only perform static analysis and identify how many values can be
(potentially) extracted at runtime. The runtime values themselves need to be
extracted by running the instrumented program.
The scripts which run the analysis rely on certain environment-specific variables to find the paths to the compiled AMPLE pass and other scripts they depend upon. The paths required by this script are the following:
export AMPLE_HOME="[path_to_ample]/ample/" # Specifies path to root of ample repo
export AMPLE_BUILD="[path_to_ample]/ample/src/c++/syscall-arg-analyzer/Debug-build.chain-overhaul" # Specifies path to build of ample
export SVF_DIR="[path_to_ample]/ample/SVF-2.2/" # Path to SVF src directory
export LLVM_DIR="[path_to_llvm]/llvm-12.0.0.obj/" # Path to LLVM build directory
export APIARG_DIR="$AMPLE_HOME/data/" # Path to folder which contains file with api-arg pairs we want to extract values for
These variable are set through a setenv.sh file located in
[AMPLE_ROOT]/src/c++/syscall-arg-analyzer. Assuming that the LLVM_DIR and
SVF_DIR have been set above you only need to change the AMPLE_HOME and
AMPLE_BUILD variables.
AMPLE_HOME: This variable should contain the path to the root AMPLE directory.
This is the main repository folder on your local system.
AMPLE_BUILD: This variable should contain the path to the local AMPLE build
directory.
Please modify these paths before proceeding to the next steps.
Our analysis takes the LLVM IR as its input. Therefore, to run our analysis on an application or library, you need to first generate its bitcode. We have provided a guide on how to do that.
AMPLE expects the developer to provide the transition point between the initialization and processing phases for its multi-phase analysis. Through its multi-phase analysis, AMPLE can determine whether a file path is initialized during the program startup or in its processing phase. To do so, you must annotate the source code; explanation here.
Also, to add instrumentations for extracting runtime-available file path values, we must link the target program's bitcode with our instrumentation functions. These functions are then invoked when an initialization instruction is identified. We have created a script for adding these instrumentations to each program:
cd [AMPLE_ROOT]/src/c++/syscall-arg-analyzer/instrumentation
clang -emit-llvm -c ample_instrument.c -o ample_instrument.bc
./instrument-all.sh [folder_with_original_bitcodes] ample_instrument.bc $AMPLE_HOME/bc.outputs [appname]
The [appname] in this command is optional and can be used to run the script for only one program.
NOTE: This script only adds the instrumentation functions to the program bitcode. We must run the LLVM pass to identify the instructions that must be instrumented and add function calls to those instrumentation functions.
We have created a runAll.sh script to run all the steps needed to generate
an AppArmor policy using AMPLE.
The runAll.sh script which is in the src/c++/syscall-arg-analyzer folder
takes care of all the options that need to be passed to AMPLE.
NOTE: After generating an instrumented LLVM bitcode, AMPLE compiles the bitcode into the final binary. For this step, we must have installed any libraries that the program depends upon so that it can be linked with the program. The dependencies for all the programs in our dataset are as follows:
sudo apt install libssl-dev libpcre2-dev libpcre2-8-0 libpam0g-dev libfastjson-dev uuid-dev libevent-dev libxml2-dev libcap-dev libdb-dev zlib1g-dev
NOTE: AMPLE needs to run the target application to extract the runtime-based
file paths. Therefore, each application must have a valid configuration file with
which it can be run. Valid configuration files have been provided for the applications
in our test set. However, in case the runtime setting of one of these applications
conflicts with another program running on the target system, it may lead to invalid
policy generation. It is critical to go through and check these issues before running
AMPLE for an application. For example, based on our provided configuration file for Nginx,
it will run on port 8080. Therefore, if another program is running on this port, you need
to modify Nginx's port before running AMPLE.
The configuration files for all applications can be found in [AMPLE_HOME]/app_run_env.
NOTE: Some programs have other environment-related properties in their
configuration file that need to be modified before being executed.
The settings have been listed below:
- Lighttpd: The lighttpd.conf file found in our app_run_env folder specifies
the user and group to run lighttpd with. You must change the user and group
defined there to a valid user and group on your system. Furthermore,
you need to change the same properties which are also used in the
setup_run_env.sh script of lighttpd. This file can be found in
[AMPLE_ROOT]/app_run_env/lighttpd.
To run the script we use the following command:
./runAll.sh [appname] instrument validate
Instead of [appname], you can either specify the application name for which you
would like to run the analysis or use 'all' to run it for all of the apps in
our dataset.
The instrument option is optional, it specifies whether an instrumented
bitcode should be generated or only the static analysis portion should be run.
If the instrument option is passed, you can also pass the validate option
to validate the generated policy by running the program.
Examples:
./runAll.sh memcached
This would run the analysis for memcached and not build an instrumented binary. The generated policy would only contains paths derived statically.
./runAll.sh memcached instrument
This would run the analysis for memcached, but also create an instrumented
binary. Even though the instrumented binary is generated, you stil need to pass
the validate option for the script to run the program and extract runtime-available
file paths as well.
./runAll.sh memcached instrument validate
This command would run all the steps and generate an AppArmor policy which contains both statically derived and dynamically derived policies.
The applications currently available (when using runAll.sh the names should have the same syntax as specified below):
By default, the following paths will be used to store the output of the analysis:
$AMPLE_HOME/logs: full log of running the analysis will be stored separately for each application in this folder (named by the app)$AMPLE_HOME/policies: the statically derived policy, and the final policy will be stored in two separate policy files in this folder$AMPLE_HOME/bc.outputs: if the instrument option is specified the instrumented bitcode will be stored in this path$AMPLE_HOME/bin.outputs: if the instrument option is specified the instrumented binary will be stored in this pathThe Python script and its respective files are located in [AMPLE_ROOT]/src/python.
To use the Python script, we first need to provide its settings through the
config.json file available in this folder.
These settings include general options that are the same for all programs across
the same OS, and application-specific options that are specified for each program
separately.
The general-purpose options include: enforce-cmd and disable-cmd which specify
how to enable and disable an AppArmor policy (this might change in different OS versions).
Another option is policy-folder which is the default path to store the generated
policies.
The next option is libc-required-libraries. The standard C library (glibc) can
dynamically load other libraries. Since we do not analyze glibc statically --- because
it cannot be compiled with LLVM --- we expect these libraries to be provided manually.
This should not change regularly. However, if it does, these library names or paths
should be specified here. If the library name pattern is provided, our script will
attempt to find a library file matching this name and add its full path. If the
path is provided, AMPLE will add that path as is to the final policy.
We also have application-specific options which are configured in the apps section
of the JSON file.
We have already provided the settings needed
for the 10 applications in our dataset. You must configure these settings when
adding a new program to the dataset. You can override some of these options
through the commandline when invoking the Python script.
Each application has six main properties which should be configured, however,
if you use the runAll.sh script to run AMPLE, it will override all the file
paths specified in this JSON file when invoking this Python script, so the correct
paths are passed to the script. Therefore, there is no need to modify these paths in
the JSON file.
"memcached": {
"llvm-policy-path": "[llvm-policy-path]",
"format" : "apparmor",
"binary-path": "/home/ssm-user/analysis/apps/memcached-1.6.9/memcached",
"runtime-paths-auto": "/tmp/ample/memcached.instrumented",
"runtime-paths-manual": {
},
"caps-manual": [
"setgid",
"setuid"
]
},
llvm-policy-path: path to the policy built by the LLVM pass --- no need to change
if the Python script is called through runAll.sh
format: for now we only support AppArmor policies, but it can be extended to
support SELinux
binary-path: path to the binary of the target application or library. This is
required to extract the libraries linked to it --- no need to change if the Python
script is called through runAll.sh
runtime-paths-auto: the path to the files where we store runtime-extracted file
path values --- no need to change if the Python script is called through runAll.sh
runtime-paths-manual: a dictionary with paths specified at runtime along with their
permissions.
caps-manual: AppArmor policies can restrict the capabilities used by a program as
well. Our current AMPLE design does not support automatically extracting program's
required capabilities, so we must add the program's needed capabilities here.
This script will be run when using the runAll.sh script.
runAll.sh ScriptThe runAll.sh script contains all the options and commands to run AMPLE for a
program. In this section we have provided details on this script and its parameters.
Since runAll.sh executes both the LLVM pass and the Python script, we have defined
the required command line options for each of these analyses in this script.
We will first describe the LLVM pass command line options that determine which
optional features of the pass should be executed. (To see a full list of all the
available runtime options for AMPLE's LLVM pass we can use:
[AMPLE_BUILD_DIR]/ample --help | grep AMPLE)
These options are defined in the following variable names:
AMPLEOPTS: The final options used to run the LLVM pass.
GENERALOPTS: These are the LLVM pass options that we are currently using when
analyzing any program. At some point, we could specify these values in the LLVM
pass source as the default values and refrain from repeating them here.
TYPEOPT: Our pass uses type-based argument filtering for reducing the spurious
indirect function call edges. This was used in the Temporal System Call Specialization
paper. We do not add this option in the general options (GENERALOPTS), because it
can lead to soundness issues for programs developed with C++.
INSTRUMENTOPT: This variable adds the -enable-instrument option which can only
be used if the pass is run through opt. It enables the instrumentation part of
the program.
PTSLOOP: This variable adds the -pts-based-depth option which is an experimental
feature that is not fully tested yet.
These are the options that are defined the same across all programs. We also have
program-specific options that are defined in the if clauses in the script.
The program-specific options are as follows:
AMPLEOPTS: This is the final variable used to run the LLVM pass. In the
program-specific options we add the transition point used for our multi-phase
analysis (more details).
LIBS: The output of our AMPLE pass is an instrumented bitcode that needs to
be compiled into the final binary before being launched. To compile the bitcode,
we also need to link it with the libraries it depends upon. This variable
contains a list of the libraries that must be linked with the program.
RUNOPTS: Generating the final policy requires executing the instrumented
binary to extract the runtime file path values. To do so, some programs must
be run with specific command line options. We specify these options through
this variable. Each runtime option must be added separately. For example,
we run need to run nginx as follows: nginx -g 'daemon off; master_process off;'
and add the options using the following:
RUNOPTS+=("-g")
RUNOPTS+=("daemon off; master_process off;")
SUDO: Some programs need root permissions to launch correctly (e.g., nginx
needs root permission if it runs on a port <1024). For these cases, we can
enable the SUDO variable so the program is executed with sudo.
We do not accept contributions on this project, as it has only been released to allow our paper's data to be reproduced.
Distributed under the <License name> License. See LICENSE for more
information.
Hamed Ghavamnia - @s_hamedgh - sghavamnia@bloomberg.net
Project Link: https://github.com/bloomberg/ample
Please consider citing our paper if you found our tool useful.
@inproceedings{amplease25,
year={2025},
booktitle={Proceedings of the 40th IEEE/ACM International Conference on Automated Software Engineering (ASE)},
title={AMPLE: Fine-grained File Access Policies for Server Applications},
author={Ghavamnia, Seyedhamed and Vanegue, Julien}
}
1 commits
C++
56.2%
Python
17.3%
HTML
13.7%
Makefile
6.5%
Shell
4.9%