static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog) { int i, index = -1, map_fd = -1, err = -EINVAL; /* retrieve a fd of our prog_array map in BPF */ map_fd = skel_map_get_fd_by_id(jmp_table.map->id); ... /* insert the program in the jump table */ err = skel_map_update_elem(map_fd, &index, &prog_fd, 0); if (err) goto out; ... if (err < 0) __hid_bpf_do_release_prog(map_fd, index); if (map_fd >= 0) close_fd(map_fd); return err; } What. The. Hell? Folks, descriptor table is a shared object. It is NOT safe to use as a scratchpad. Another thread might do whatever it bloody wants to anything inserted there. It may close your descriptor, it may replace it with something entirely unrelated to what you've placed there, etc. This is fundamentally broken. The same goes for anything that tries to play similar games. Don't use descriptor tables that way. Kernel-side descriptors should be used only for marshalling - they can be passed by userland (and should be resolved to struct file *, with no expectations that repeated call of fget() would yield the same pointer) and they can be returned _to_ userland - after you've allocated them and associated them with struct file *. Using them as handles for internal objects is an equivalent of playing in the traffic - think of it as evolution in action.