Skip to content

CFI Type Mismatch (Function Pointer Cast) in ARM Linux Parser #402

Description

@mhansen

Hi, I spotted this when trying to run an Android application under UBSan. Upon digging, I found this issue:

A Clang Control Flow Integrity (CFI) type mismatch occurs in src/arm/linux/cpuinfo.c when invoking the parse_line callback through the cpuinfo_line_callback function pointer. This triggers a runtime SIGTRAP when CFI is enabled.

The issue is that parse_line is defined with a specific structure pointer as its third argument, but it is cast to cpuinfo_line_callback which expects void* for that argument.

In src/arm/linux/cpuinfo.c we have parse_line which takes a proc_cpuinfo_parser_state argument

static bool parse_line(
const char* line_start,
const char* line_end,
struct proc_cpuinfo_parser_state state[restrict static 1],
uint64_t line_number) {

In cpuinfo_arm_linux_parse_proc_cpuinfo:

cpuinfo/src/arm/linux/api.h

Lines 297 to 301 in ae54436

CPUINFO_INTERNAL bool cpuinfo_arm_linux_parse_proc_cpuinfo(
char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
uint32_t max_processors_count,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]);

The callback type is defined in src/linux/api.h as:

typedef bool (*cpuinfo_line_callback)(const char*, const char*, void*, uint64_t);

The problem is specifically here, where we cast parse_line to cpuinfo_line_callback. These functions have different signatures:

return cpuinfo_linux_parse_multiline_file(
"/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state);
}

Calling a function through a pointer of a different type (even if they are pointer-compatible in some ABIs) is Undefined Behavior in C and is flagged by Clang's -fsanitize=cfi-icall.

Suggested fix: Change parse_line to accept void* for the context argument, and cast it inside the function. Remove the cast when passing it to cpuinfo_linux_parse_multiline_file.

--- a/src/arm/linux/cpuinfo.c
+++ b/src/arm/linux/cpuinfo.c
@@ -726,8 +726,9 @@
 static bool parse_line(
 	const char* line_start,
 	const char* line_end,
-	struct proc_cpuinfo_parser_state state[restrict static 1],
+	void* context,
 	uint64_t line_number) {
+	struct proc_cpuinfo_parser_state* restrict state = (struct proc_cpuinfo_parser_state*)context;
 	/* Empty line. Skip. */
 	if (line_start == line_end) {
 		return true;
@@ -1019,5 +1018,5 @@
 		.processors = processors,
 	};
 	return cpuinfo_linux_parse_multiline_file(
-		"/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state);
+		"/proc/cpuinfo", BUFFER_SIZE, parse_line, &state);
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions