A Treasure Map for Static Analysis in C Language

A Treasure Map for Static Analysis in C LanguageA Treasure Map for Static Analysis in C LanguageA Treasure Map for Static Analysis in C LanguageA Treasure Map for Static Analysis in C Language

At the end of the article on pointer handling (click here to view), we mentioned the functionality of generating side-effects for pointers:

#include <stdio.h>
int test(int a,int b,int* c){
    *c = a + b;
    return 0;
}
int main(){
    int a=1,b=2,c;
    test(a,b,&c);
    printf("%d\n",c);
    return 0;
}

When analyzing data flow using printf(* #-> as $a), since the variable <span>c</span> and the return value of <span>test(a,b,&c)</span> are unrelated, we will use side-effects to connect the data flow, which here is <span>side-effect(add(Parameter-a, Parameter-b), c)</span>.

When we define our own functions, we can clearly know their complete function signatures (i.e., return type, function name, parameter types, etc.). However, when using <span>#include</span> to import external library functions (such as C standard library functions), it becomes complex to accurately obtain their signatures.

To correctly handle these external functions during static analysis, we compiled a batch of common basic libraries using the IRify tool and pre-defined the signature information of these library functions in the preprocessing stage of c2ssa. The current approach is to use a hard-coded method to forcibly inject empty functions (stub functions) that exactly match the target library function signatures at compile time.

For example, the following code for sprintf will generate an extra empty function:

#include <stdio.h>
int sprintf(char *str, const char *format, ...){}
void vulnerable_file_list(const char *directory) {
    char command[256];
    sprintf(command, "ls -la %s", directory);
    println(command); // side-effect(Parameter-directory, command)
}

When hard-coding the function signatures of C library functions, we thought about using macro expansion to solve the problem once and for all.

A Treasure Map for Static Analysis in C Language

The C language macro expansion preprocessing system is one of the core components of the YAK C2SSA module, responsible for automatically processing macro definitions, conditional compilation, and header file inclusion before C code compilation. This system transparently performs macro expansion during file reading through a file system hook mechanism, ensuring that subsequent SSA transformations can handle the expanded macro code.

The system adopts the design pattern of file system hooks, intercepting file read operations and automatically executing macro preprocessing before returning the source code.

File read request
    ↓File system hook intercepts (.c/.h files)
    ↓Extract #include directives
    ↓Ensure header files exist (create temporary header files)
    ↓Call C preprocessor (gcc/clang -E)
    ↓Return preprocessed code
func newCPreprocessFS(underlying fi.FileSystem) fi.FileSystem {
    // 1. Set up header file environment
    if err := setupHeaderFiles(underlying); err != nil {
        log.Warnf("setupHeaderFiles failed: %v", err)
        return underlying
    }
    // 2. Create hook file system
    hookFS := filesys.NewHookFS(underlying)
    // 3. Register read hook
    hookFS.AddReadHook(&filesys.ReadHook{
        Matcher: filesys.SuffixMatcher(".c", ".h"),
        AfterRead: func(ctx *filesys.ReadHookContext, data []byte) ([]byte, error) {
            // Macro preprocessing logic
        },
    })
    return hookFS
}

A Treasure Map for Static Analysis in C Language

During the macro expansion process, to maintain the integrity of the user’s source code without modification, we choose to complete all macro expansion-related operations in a temporary directory:

func initTemp() error {
    tmpDir, err := os.MkdirTemp("", "c_headers_*")
    if err != nil {
        return fmt.Errorf("failed to create temp directory: %w", err)
    }
    globalTempDir = tmpDir
    return nil
}

The system creates a temporary directory at startup to store:

  • Header files in the project (<span><span>.h</span></span>, <span><span>.in</span></span>)

  • Automatically created system header file placeholders

  • Temporary source files during preprocessing

The system scans all header files in the underlying file system and copies user-defined header files to the corresponding directory. When processing <span>#include</span> directives in the code, we provide two configuration options: one is to copy and use system C library files, and the other is to create empty file placeholders.

Currently, we have set “create empty file placeholders” as the default configuration because we found that some users may not have a complete C library environment during testing, or the current machine permissions may not allow copying system C library files.

Including C standard library functions in macro expansion can provide the most accurate analysis results (as it contains the most authentic signatures and behaviors of functions), but the cost is that it generates and displays a large amount of complex internal macro expansion code in the frontend, making the code view cumbersome and not conducive for developers to quickly browse and understand the core logic. Therefore, in typical code reading and debugging scenarios, hiding library function macro expansions can keep the interface clean.

A Treasure Map for Static Analysis in C Language

The system detects available C preprocessors by priority:

gcc: GNU Compiler Collection (preferred)

clang: LLVM Compiler

cc: System default C compiler

A Treasure Map for Static Analysis in C Language

Input Code:

#define MAX_SIZE 1024
int main() {
    int size = MAX_SIZE;
    return 0;
}

Processing Flow:

1. File system hook intercepts the reading of <span>main.c</span>

2. Extract include directives (none)

3. Create temporary file and write source code

4. Call <span>gcc -E -P -nostdinc ...</span>

5. Return preprocessed result

Output Code:

intmain() {
    int size = 1024;
    return0;
}

A Treasure Map for Static Analysis in C Language

Input Code:

#define MIN(a,b) ((a)<(b)?(a):(b))
int main() {
    int x = 10, y = 20;
    int min = MIN(x, y);
    return 0;
}

Output Code:

int main() {
    int x = 10, y = 20;
    int min = ((x)<(y)?(x):(y));
    return 0;
}

A Treasure Map for Static Analysis in C Language

Input Code:

#include <myheader.h>
#define VALUE 42
int main() {
    return VALUE;
}

Processing Flow:

1. Extract <span>#include</span><span> <myheader.h></span>

2. Create <span>globalTempDir/include/myheader.h</span> empty file

3. Add include path <span>-I globalTempDir/include</span>

4. Execute preprocessing

A Treasure Map for Static Analysis in C Language

Currently, Syntaxflow’s support for C language is very limited. For example, the goal is to find an overflow vulnerability, according to Syntaxflow’s approach, one should look for the usage locations of taint functions, and then find the top definitions to analyze whether they intersect with controllable data flows.

However, sometimes the copying in C language makes it difficult to start, as in the following case:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void vulnerable_function() {
    char local_buf[100];
    unsigned char input_len;
    input_len = get_input_length();
    char *input_str = get_input_string();
    if (input_len >= 4 && input_len <= 8) {
        for (int i = 0; i < input_len && i < sizeof(local_buf) - 1; i++) {
            local_buf[i] = input_str[i];
        }
        if (input_len < sizeof(local_buf)) {
            local_buf[input_len] = '\0';
        } else {
            local_buf[sizeof(local_buf) - 1] = '\0';
        }
    }
}

In C language, using <span><span>for</span></span> loops with array indexing <span><span>arr[i]</span></span> is a very basic and efficient programming pattern. However, its safety is not guaranteed by the language itself, but entirely relies on the programmer’s careful coding.

This kind of code leaves Syntaxflow with no starting point, as the current Syntaxflow rules heavily depend on various symbols in the code. We are currently actively learning from Fortify how to handle related issues, which may be addressed in future versions.

In addition to the more common overflow issues, there are many dangerous functions in C language that can be deemed high-risk once used:

gets()    as $high;
scanf(*<slice(index=0)>?{have: "%s"} as $high);
vscanf(*<slice(index=1)>?{have: "%s"} as $high);
fscanf(*<slice(index=1)>?{have: "%s"} as $high);
vfscanf(*<slice(index=1)>?{have: "%s"} as $high);
sscanf(*<slice(index=1)>?{have: "%s"} as $high);
vsscanf(*<slice(index=1)>?{have: "%s"} as $high);

For functions like <span>printf</span>, it is necessary to check whether there are format strings at the call points. If they can be controlled by the user, they should be marked as high-risk:

printf(*<slice(index=0)> #-> as $sink);
fprintf(*<slice(index=1)> #-> as $sink);
sprintf(*<slice(index=1)> #-> as $sink);
snprintf(*<slice(index=2)> #-> as $sink);
syslog(*<slice(index=1)> #-> as $sink);
$sink?{!opcode:const} as $high

A Treasure Map for Static Analysis in C Language

Currently, SyntaxFlow has limitations in analyzing complex memory vulnerabilities such as memory leaks, UAF (Use After Free), and Double Free. Although the underlying YAK infrastructure can express relatively complex control flow information, the front end of SyntaxFlow currently mainly focuses on displaying and tracking data flow, and has not fully utilized this control flow information.

The future plan is to introduce a control flow analysis mechanism based on basic blocks, aiming to ultimately achieve an interactive control flow graph similar to that in IDA, thereby revealing the program execution paths and branching logic more intuitively.

END

Official YAK Resources

Official Yak Language Tutorial:https://yaklang.com/docs/intro/Yakit Video Tutorial:https://space.bilibili.com/437503777Github Download Link:https://github.com/yaklang/yakitYakit Official Download Link:https://yaklang.com/Yakit Installation Documentation:https://yaklang.com/products/download_and_installYakit Usage Documentation:https://yaklang.com/products/intro/Common Questions Quick Reference:https://yaklang.com/products/FAQ

A Treasure Map for Static Analysis in C Language

Leave a Comment