Skip to content

Commit 6633a82

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents a1ca8a9 + 7b382a2 commit 6633a82

6 files changed

Lines changed: 572 additions & 25 deletions

File tree

src/ebpf_c_codegen.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2581,7 +2581,10 @@ and generate_assignment ctx dest_val expr is_const =
25812581
| IRValue src_val ->
25822582
(* Simple value assignment *)
25832583
let dest_str = generate_c_value ctx dest_val in
2584-
let src_str = generate_c_value ctx src_val in
2584+
(* Auto-dereference map access to get the value, not the pointer *)
2585+
let src_str = (match src_val.value_desc with
2586+
| IRMapAccess (_, _, _) -> generate_c_value ~auto_deref_map_access:true ctx src_val
2587+
| _ -> generate_c_value ctx src_val) in
25852588
emit_line ctx (sprintf "%s%s = %s;" assignment_prefix dest_str src_str)
25862589
| _ ->
25872590
(* Other expressions *)

src/ir_generator.ml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,12 +2825,15 @@ let lower_multi_program ast symbol_table source_name =
28252825
let ir_map = lower_map_declaration symbol_table map_decl in
28262826
((decl, `Map ir_map) :: decls, ir_map :: maps, vars)
28272827
| Ast.GlobalVarDecl var_decl when is_global_var_map var_decl ->
2828+
(* When a global variable has a map type, convert it to a map definition only.
2829+
Do NOT create a corresponding IR global variable, as that causes the map
2830+
to be incorrectly treated as a pinned global variable in userspace codegen. *)
28282831
let ir_map = match convert_global_var_to_map symbol_table var_decl with
28292832
| Some map -> map
28302833
| None -> failwith "Expected map conversion to succeed"
28312834
in
2832-
let ir_var = lower_global_variable_declaration symbol_table var_decl in
2833-
((decl, `MapFromGlobalVar (ir_map, ir_var)) :: decls, ir_map :: maps, ir_var :: vars)
2835+
(* Only track as map, not as global variable *)
2836+
((decl, `MapFromGlobalVar ir_map) :: decls, ir_map :: maps, vars)
28342837
| Ast.GlobalVarDecl var_decl ->
28352838
let ir_var = lower_global_variable_declaration symbol_table var_decl in
28362839
((decl, `GlobalVar ir_var) :: decls, maps, ir_var :: vars)
@@ -3136,10 +3139,11 @@ let lower_multi_program ast symbol_table source_name =
31363139
match processed with
31373140
| `Map ir_map ->
31383141
add_source_declaration (IRDeclMapDef ir_map) pos
3139-
| `MapFromGlobalVar (ir_map, ir_var) ->
3140-
(* For global variables with map types, add both the map and the variable *)
3141-
add_source_declaration (IRDeclMapDef ir_map) pos;
3142-
add_source_declaration (IRDeclGlobalVarDef ir_var) pos
3142+
| `MapFromGlobalVar ir_map ->
3143+
(* For global variables with map types, only add the map definition.
3144+
Do NOT add a global variable declaration, as it would incorrectly be treated
3145+
as a pinned global variable in userspace codegen. *)
3146+
add_source_declaration (IRDeclMapDef ir_map) pos
31433147
| `GlobalVar ir_var ->
31443148
add_source_declaration (IRDeclGlobalVarDef ir_var) pos
31453149
| `Other decl ->

src/userspace_codegen.ml

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,7 +2610,13 @@ let generate_c_function_from_ir ?(global_variables = []) ?(base_name = "") ?(con
26102610
let pinned_globals_vars = List.filter (fun gv -> gv.is_pinned) global_variables in
26112611
let has_pinned_globals = List.length pinned_globals_vars > 0 in
26122612

2613-
let setup_call = if needs_object_loading && (List.length config_declarations > 0 || func_usage.uses_map_operations || func_usage.uses_exec || has_pinned_globals) then
2613+
(* Check if there are any pinned maps that need setup *)
2614+
let has_pinned_maps = match ir_multi_prog with
2615+
| Some multi_prog -> List.exists (fun map -> map.pin_path <> None) (Ir.get_global_maps multi_prog)
2616+
| None -> false
2617+
in
2618+
2619+
let setup_call = if needs_object_loading && (List.length config_declarations > 0 || func_usage.uses_map_operations || func_usage.uses_exec || has_pinned_globals || has_pinned_maps) then
26142620
let all_setup_parts = List.filter (fun s -> s <> "") [
26152621
(if has_pinned_globals then
26162622
let project_name = base_name in
@@ -2637,8 +2643,8 @@ let generate_c_function_from_ir ?(global_variables = []) ?(base_name = "") ?(con
26372643
}
26382644
}|} pin_path pin_path
26392645
else "");
2640-
(* Include all_setup_code only once for maps, config, struct_ops, and ringbuf *)
2641-
(if func_usage.uses_map_operations || func_usage.uses_exec || List.length config_declarations > 0 then all_setup_code else "");
2646+
(* Include all_setup_code for maps (including pinned maps), config, struct_ops, and ringbuf *)
2647+
(if func_usage.uses_map_operations || func_usage.uses_exec || List.length config_declarations > 0 || has_pinned_maps then all_setup_code else "");
26422648
] in
26432649
if all_setup_parts <> [] then "\n" ^ String.concat "\n" all_setup_parts else ""
26442650
else "" in
@@ -2811,6 +2817,19 @@ let generate_pinned_globals_support _project_name global_variables =
28112817

28122818
(** Generate ring buffer event handler functions *)
28132819
let generate_ringbuf_handlers_from_registry (registry : Ir.ir_ring_buffer_registry) ~dispatch_used =
2820+
(* Generate forward declarations for callback functions *)
2821+
let forward_declarations = List.map (fun rb_decl ->
2822+
let ringbuf_name = rb_decl.rb_name in
2823+
let value_type = c_type_from_ir_type rb_decl.rb_value_type in
2824+
let handler_name = match List.assoc_opt ringbuf_name registry.event_handler_registrations with
2825+
| Some handler -> handler
2826+
| None ->
2827+
(* Try callback function naming convention: {ringbuf_name}_callback *)
2828+
ringbuf_name ^ "_callback"
2829+
in
2830+
sprintf "int %s(%s *event);" handler_name value_type
2831+
) registry.ring_buffer_declarations |> String.concat "\n" in
2832+
28142833
let event_handlers = List.map (fun rb_decl ->
28152834
let ringbuf_name = rb_decl.rb_name in
28162835
let value_type = c_type_from_ir_type rb_decl.rb_value_type in
@@ -2836,7 +2855,11 @@ static int %s_event_handler(void *ctx, void *data, size_t data_sz) {
28362855
else "" in
28372856

28382857
(* Only generate event handlers if dispatch is actually used *)
2839-
let final_event_handlers = if dispatch_used then event_handlers else "" in
2858+
let final_event_handlers = if dispatch_used then
2859+
if List.length registry.ring_buffer_declarations > 0 then
2860+
sprintf "\n// Forward declarations for ring buffer callbacks\n%s\n%s" forward_declarations event_handlers
2861+
else ""
2862+
else "" in
28402863

28412864
final_event_handlers ^ combined_rb_declaration
28422865

@@ -2957,23 +2980,30 @@ let generate_unified_map_setup_code ?(obj_var="obj->obj") maps =
29572980
else map :: acc
29582981
) [] maps |> List.rev in
29592982

2960-
List.map (fun map ->
2983+
let map_setups = List.map (fun map ->
29612984
(* Always load from eBPF object first, then handle pinning if needed *)
29622985
let pin_logic = match map.pin_path with
29632986
| Some pin_path ->
2987+
(* Extract directory path from pin_path *)
2988+
let dir_path = Filename.dirname pin_path in
2989+
(* Generate unique variable name for each map's existing_fd *)
29642990
Printf.sprintf {|
29652991
// Check if map is already pinned
2966-
int existing_fd = bpf_obj_get("%s");
2967-
if (existing_fd >= 0) {
2968-
%s_fd = existing_fd;
2992+
int %s_existing_fd = bpf_obj_get("%s");
2993+
if (%s_existing_fd >= 0) {
2994+
%s_fd = %s_existing_fd;
29692995
} else {
2970-
// Map not pinned yet, pin it now
2996+
// Map not pinned yet, create directory and pin it
2997+
if (ensure_bpf_dir("%s") < 0) {
2998+
fprintf(stderr, "Failed to create directory %s: %%s\n", strerror(errno));
2999+
return 1;
3000+
}
29713001
if (bpf_map__pin(%s_map, "%s") < 0) {
29723002
fprintf(stderr, "Failed to pin %s map to %s\n");
29733003
return 1;
29743004
}
29753005
%s_fd = bpf_map__fd(%s_map);
2976-
}|} pin_path map.map_name map.map_name pin_path map.map_name pin_path map.map_name map.map_name
3006+
}|} map.map_name pin_path map.map_name map.map_name map.map_name dir_path dir_path map.map_name pin_path map.map_name pin_path map.map_name map.map_name
29773007
| None ->
29783008
Printf.sprintf {|
29793009
// Non-pinned map, just get file descriptor
@@ -2989,7 +3019,9 @@ let generate_unified_map_setup_code ?(obj_var="obj->obj") maps =
29893019
fprintf(stderr, "Failed to get fd for %s map\n");
29903020
return 1;
29913021
}|} map.map_name map.map_name obj_var map.map_name map.map_name map.map_name pin_logic map.map_name map.map_name
2992-
) deduplicated_maps |> String.concat "\n"
3022+
) deduplicated_maps in
3023+
3024+
String.concat "\n" map_setups
29933025

29943026
(** Generate config struct definition from config declaration - reusing eBPF logic *)
29953027
let generate_config_struct_from_decl (config_decl : Ast.config_declaration) =
@@ -3446,8 +3478,14 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ta
34463478
maps_for_exec (* Use all global maps directly for exec *)
34473479
else used_global_maps in
34483480

3481+
(* Check if there are any pinned maps - this affects which headers we need *)
3482+
let has_any_pinned_maps = List.exists (fun map -> map.pin_path <> None) global_maps in
3483+
3484+
(* For header generation, use all global maps if there are pinned maps, otherwise use the filtered list *)
3485+
let maps_for_headers = if has_any_pinned_maps then global_maps else used_global_maps_with_exec in
3486+
34493487
let uses_bpf_functions = all_usage.uses_load || all_usage.uses_attach || all_usage.uses_detach in
3450-
let base_includes = generate_headers_for_maps ~uses_bpf_functions used_global_maps_with_exec in
3488+
let base_includes = generate_headers_for_maps ~uses_bpf_functions maps_for_headers in
34513489
let additional_includes = {|#include <stdbool.h>
34523490
#include <stdint.h>
34533491
#include <inttypes.h>
@@ -3520,8 +3558,12 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ta
35203558
else "" in
35213559

35223560
(* Generate setup code first for use in main function *)
3523-
let map_setup_code = if all_usage.uses_map_operations || all_usage.uses_exec then
3524-
generate_unified_map_setup_code used_global_maps_with_exec
3561+
(* Check if there are any pinned maps that need setup *)
3562+
let has_pinned_maps = List.exists (fun map -> map.pin_path <> None) global_maps in
3563+
let map_setup_code = if all_usage.uses_map_operations || all_usage.uses_exec || has_pinned_maps then
3564+
(* For pinned maps, we need to include all of them in setup, not just used ones *)
3565+
let maps_for_setup = if has_pinned_maps then global_maps else used_global_maps_with_exec in
3566+
generate_unified_map_setup_code maps_for_setup
35253567
else "" in
35263568

35273569
(* Generate pinned globals support *)
@@ -3588,8 +3630,9 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ta
35883630

35893631

35903632

3591-
let map_fd_declarations = if all_usage.uses_map_operations || all_usage.uses_exec then
3592-
generate_map_fd_declarations used_global_maps_with_exec
3633+
let map_fd_declarations = if all_usage.uses_map_operations || all_usage.uses_exec || has_pinned_maps then
3634+
let maps_for_fd = if has_pinned_maps then global_maps else used_global_maps_with_exec in
3635+
generate_map_fd_declarations maps_for_fd
35933636
else "" in
35943637

35953638
(* Generate config map file descriptors if there are config declarations *)
@@ -3666,6 +3709,9 @@ void cleanup_bpf_maps(void) {
36663709

36673710
(* Only generate BPF helper functions when they're actually used *)
36683711
let bpf_helper_functions =
3712+
(* Check if there are any pinned maps in the global maps *)
3713+
let has_pinned_maps = List.exists (fun map -> map.pin_path <> None) global_maps in
3714+
36693715
let load_function = generate_load_function_with_tail_calls base_name all_usage tail_call_analysis all_setup_code kfunc_dependencies (Ir.get_global_variables ir_multi_prog) in
36703716

36713717
(* Global attachment storage (generated only when attach/detach are used) *)
@@ -4140,7 +4186,40 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
41404186
) maps_for_exec |> String.concat "\n")
41414187
else "" in
41424188
4143-
let functions_list = List.filter (fun s -> s <> "") [attachment_storage; load_function; attach_function; detach_function; daemon_function; exec_function] in
4189+
(* Generate directory creation helper if there are pinned maps *)
4190+
let mkdir_helper_function = if has_pinned_maps then
4191+
{|// Helper function to create directory recursively
4192+
static int ensure_bpf_dir(const char *path) {
4193+
char tmp[4096];
4194+
char *p = NULL;
4195+
size_t len;
4196+
4197+
if (!path || strlen(path) >= sizeof(tmp)) {
4198+
fprintf(stderr, "ensure_bpf_dir: path too long or NULL\n");
4199+
return -1;
4200+
}
4201+
4202+
snprintf(tmp, sizeof(tmp), "%s", path);
4203+
len = strlen(tmp);
4204+
if (len > 0 && tmp[len - 1] == '/') tmp[len - 1] = 0;
4205+
4206+
for (p = tmp + 1; *p; p++) {
4207+
if (*p == '/') {
4208+
*p = 0;
4209+
if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
4210+
return -1;
4211+
}
4212+
*p = '/';
4213+
}
4214+
}
4215+
if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
4216+
return -1;
4217+
}
4218+
return 0;
4219+
}|}
4220+
else "" in
4221+
4222+
let functions_list = List.filter (fun s -> s <> "") [mkdir_helper_function; attachment_storage; load_function; attach_function; detach_function; daemon_function; exec_function] in
41444223
if functions_list = [] && bpf_obj_decl = "" then ""
41454224
else
41464225
sprintf "\n/* BPF Helper Functions (generated only when used) */\n%s\n\n%s"

tests/test_ebpf_c_codegen.ml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,37 @@ let test_global_map_redefinition_fix () =
11461146

11471147
()
11481148

1149+
(** Test map access auto-dereference in variable assignment (covers lines 2559-2566) *)
1150+
let test_map_access_auto_deref_in_assignment () =
1151+
let ctx = create_c_context () in
1152+
1153+
(* Build: count = my_map[user_key]
1154+
IRMapAccess carries the raw lookup-pointer as its underlying value. *)
1155+
let key_val = make_ir_value (IRVariable "user_key") IRU32 test_pos in
1156+
let underlying_desc = IRVariable "map_ptr_0" in
1157+
let underlying_type = IRPointer (IRU64, make_bounds_info ()) in
1158+
let src_val = make_ir_value
1159+
(IRMapAccess ("my_map", key_val, (underlying_desc, underlying_type)))
1160+
IRU64 test_pos in
1161+
1162+
let dest_val = make_ir_value (IRVariable "count") IRU64 test_pos in
1163+
let assign_instr = make_ir_instruction
1164+
(IRAssign (dest_val, make_ir_expr (IRValue src_val) IRU64 test_pos))
1165+
test_pos in
1166+
1167+
generate_c_instruction ctx assign_instr;
1168+
1169+
let output = String.concat "\n" ctx.output_lines in
1170+
1171+
(* auto_deref_map_access:true emits a guarded dereference:
1172+
count = ({ __u64 __val = {0}; if (map_ptr_0) { __val = *(map_ptr_0); } __val; }); *)
1173+
check bool "dest variable present in output" true (contains_substr output "count =");
1174+
check bool "__val used for safe dereference" true (contains_substr output "__val");
1175+
check bool "null-guard if-check emitted" true (contains_substr output "if (map_ptr_0)");
1176+
check bool "pointer dereference emitted" true (contains_substr output "*(map_ptr_0)");
1177+
(* Without the fix this branch would fall through to the raw-pointer path *)
1178+
check bool "no raw pointer assignment" false (contains_substr output "count = map_ptr_0")
1179+
11491180
(** Tests for tail call fallback return fix.
11501181
These tests verify that every match arm containing a tail call emits an
11511182
explicit fallback return statement so the eBPF verifier can confirm that
@@ -1305,6 +1336,8 @@ let suite =
13051336
("eBPF function generation bug fix", `Quick, test_ebpf_function_generation_bug_fix);
13061337
(* Test to prevent global variable map redefinition regression *)
13071338
("Global map redefinition fix", `Quick, test_global_map_redefinition_fix);
1339+
(* Coverage for IRMapAccess auto-dereference path in generate_assignment *)
1340+
("Map access auto-deref in assignment", `Quick, test_map_access_auto_deref_in_assignment);
13081341
(* Tail call fallback return fix - verifier requires explicit return after bpf_tail_call() *)
13091342
("Tail call fallback: constant arm XDP context", `Quick, test_tail_call_fallback_constant_arm_xdp);
13101343
("Tail call fallback: default arm TC context", `Quick, test_tail_call_fallback_default_arm_tc);

0 commit comments

Comments
 (0)