@@ -3776,7 +3776,7 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ta
37763776 (* For header generation, use all global maps if there are pinned maps, otherwise use the filtered list *)
37773777 let maps_for_headers = if has_any_pinned_maps then global_maps else used_global_maps_with_exec in
37783778
3779- let uses_bpf_functions = all_usage.uses_load || all_usage.uses_attach || all_usage.uses_detach in
3779+ let uses_bpf_functions = all_usage.uses_load || all_usage.uses_attach || all_usage.uses_detach || all_usage.uses_attach_perf in
37803780 let base_includes = generate_headers_for_maps ~uses_bpf_functions maps_for_headers in
37813781 let bpf_attach_includes = if uses_bpf_functions then
37823782 " #include <sys/ioctl.h>\n "
@@ -4061,38 +4061,8 @@ struct attachment_entry {
40614061static struct attachment_entry * attached_programs = NULL ;
40624062static pthread_mutex_t attachment_mutex = PTHREAD_MUTEX_INITIALIZER ;
40634063
4064- // Helper function to find attachment entry
4065- static struct attachment_entry * find_attachment(int prog_fd) {
4066- pthread_mutex_lock(& attachment_mutex);
4067- struct attachment_entry * current = attached_programs;
4068- while (current) {
4069- if (current->prog_fd == prog_fd) {
4070- pthread_mutex_unlock(& attachment_mutex);
4071- return current;
4072- }
4073- current = current->next;
4074- }
4075- pthread_mutex_unlock(& attachment_mutex);
4076- return NULL ;
4077- }
4078-
4079- // Helper function to remove attachment entry
4080- static void remove_attachment(int prog_fd) {
4081- pthread_mutex_lock(& attachment_mutex);
4082- struct attachment_entry ** current = & attached_programs;
4083- while (* current) {
4084- if ((* current)->prog_fd == prog_fd) {
4085- struct attachment_entry *to_remove = *current;
4086- *current = (* current)->next;
4087- free(to_remove);
4088- break;
4089- }
4090- current = &(* current)->next;
4091- }
4092- pthread_mutex_unlock(&attachment_mutex);
4093- }
4094-
4095- // Helper function to add attachment entry
4064+ // Helper function to add attachment entry.
4065+ // Duplicate check is performed atomically under the same lock as insertion.
40964066static int add_attachment(int prog_fd, const char * target, uint32_t flags,
40974067 struct bpf_link * link, int ifindex, int perf_fd,
40984068 enum bpf_prog_type type ) {
@@ -4112,6 +4082,17 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
41124082 entry->type = type ;
41134083
41144084 pthread_mutex_lock(& attachment_mutex);
4085+ /* Reject duplicate insertions atomically */
4086+ struct attachment_entry * existing = attached_programs;
4087+ while (existing) {
4088+ if (existing->prog_fd == prog_fd) {
4089+ pthread_mutex_unlock(& attachment_mutex);
4090+ free(entry);
4091+ fprintf(stderr, " Program with fd %d is already attached. Use detach() first.\n " , prog_fd);
4092+ return - 1 ;
4093+ }
4094+ existing = existing->next;
4095+ }
41154096 entry->next = attached_programs;
41164097 attached_programs = entry;
41174098 pthread_mutex_unlock(& attachment_mutex);
@@ -4141,12 +4122,6 @@ static struct bpf_program *find_prog_by_fd(int prog_fd) {
41414122 return - 1 ;
41424123 }
41434124
4144- // Check if program is already attached
4145- if (find_attachment(prog_fd)) {
4146- fprintf(stderr, "Program with fd %d is already attached. Use detach() first.\n", prog_fd);
4147- return -1;
4148- }
4149-
41504125 // Get program type from file descriptor
41514126 struct bpf_prog_info info = {};
41524127 uint32_t info_len = sizeof(info);
@@ -4353,8 +4328,21 @@ static struct bpf_program *find_prog_by_fd(int prog_fd) {
43534328 return;
43544329 }
43554330
4356- // Find the attachment entry
4357- struct attachment_entry *entry = find_attachment(prog_fd);
4331+ /* Atomically extract the entry from the list so concurrent detach/ perf_read
4332+ * cannot dereference a freed pointer. */
4333+ pthread_mutex_lock(& attachment_mutex);
4334+ struct attachment_entry * entry = NULL ;
4335+ struct attachment_entry ** cur = & attached_programs;
4336+ while (* cur) {
4337+ if ((* cur)->prog_fd == prog_fd) {
4338+ entry = *cur;
4339+ *cur = entry->next;
4340+ break;
4341+ }
4342+ cur = &(* cur)->next;
4343+ }
4344+ pthread_mutex_unlock(&attachment_mutex);
4345+
43584346 if (!entry) {
43594347 fprintf(stderr, "No active attachment found for program fd %%d\n", prog_fd);
43604348 return;
@@ -4412,8 +4400,7 @@ static struct bpf_program *find_prog_by_fd(int prog_fd) {
44124400 break;
44134401 }
44144402
4415- // Remove from tracking
4416- remove_attachment(prog_fd);
4403+ free(entry);
44174404}|} detach_perf_case
44184405 else "" in
44194406
@@ -4619,8 +4606,13 @@ int ks_attach_perf_event(int prog_fd, ks_perf_options opts, int flags) {
46194606 fprintf(stderr, "Invalid program file descriptor: %d\n", prog_fd);
46204607 return -1;
46214608 }
4622- if (find_attachment(prog_fd)) {
4623- fprintf(stderr, "Program with fd %d is already attached. Use detach() first.\n", prog_fd);
4609+ /* Verify the program is actually a @perf_event program */
4610+ struct bpf_prog_info prog_info = {};
4611+ uint32_t info_len = sizeof(prog_info);
4612+ if (bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len) == 0 &&
4613+ prog_info.type != BPF_PROG_TYPE_PERF_EVENT) {
4614+ fprintf(stderr, "ks_attach_perf_event: fd %d is not a @perf_event program (type=%u)\n",
4615+ prog_fd, prog_info.type);
46244616 return -1;
46254617 }
46264618
@@ -4693,16 +4685,29 @@ int64_t ks_read_perf_count(int perf_fd) {
46934685/* Read the counter for the perf_event program bound to prog_fd.
46944686 * Looks up the perf_fd from the attachment table and calls ks_read_perf_count. */
46954687int64_t ks_perf_read(int prog_fd) {
4696- struct attachment_entry *entry = find_attachment(prog_fd);
4697- if (!entry) {
4688+ /* Read perf_fd under the lock so the entry cannot be freed concurrently */
4689+ pthread_mutex_lock(&attachment_mutex);
4690+ int found = 0;
4691+ int perf_fd = -1;
4692+ struct attachment_entry *cur = attached_programs;
4693+ while (cur) {
4694+ if (cur->prog_fd == prog_fd) {
4695+ found = 1;
4696+ perf_fd = cur->perf_fd;
4697+ break;
4698+ }
4699+ cur = cur->next;
4700+ }
4701+ pthread_mutex_unlock(&attachment_mutex);
4702+ if (!found) {
46984703 fprintf(stderr, "ks_perf_read: no active attachment for program fd %d\n", prog_fd);
46994704 return -1;
47004705 }
4701- if (entry-> perf_fd < 0) {
4706+ if (perf_fd < 0) {
47024707 fprintf(stderr, "ks_perf_read: program fd %d is not a perf_event program\n", prog_fd);
47034708 return -1;
47044709 }
4705- return ks_read_perf_count(entry-> perf_fd);
4710+ return ks_read_perf_count(perf_fd);
47064711}
47074712
47084713/* Print the current counter value for a named event to stdout.
0 commit comments