Skip to content

Commit 6fbaf05

Browse files
committed
Make file helpers worker-safe
File type detection kept libmagic handles, the udev handle and device-name cache, ELF header scratch storage, and fanotify response probing in shared mutable state. Those paths would race once multiple decision workers can evaluate events concurrently. Move the libmagic handles and udev/device cache into decision_context so each future worker can own its helper state. Keep ELF header scratch storage on the gather_elf stack, and make FAN_INFO response probing an explicit startup requirement before the decision thread starts. Invalid response-probe initialization is fatal during fanotify startup, while unsupported FAN_INFO leaves response_info_supported safely initialized to 0. Updated file_type_detect_test to initialize and inspect the default decision context instead of the removed libmagic globals.
1 parent 5b84623 commit 6fbaf05

6 files changed

Lines changed: 159 additions & 63 deletions

File tree

src/daemon/notify.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,13 @@ int init_fanotify(const conf_t *conf, mlist *m)
247247
exit(1);
248248
}
249249

250+
if (reply_event_init(fd)) {
251+
close(fd);
252+
q_close(q);
253+
q = NULL;
254+
exit(1);
255+
}
256+
250257
// Start decision thread so its ready when first event comes
251258
rpt_interval = conf->report_interval;
252259
int rc = pthread_create(&decision_thread, NULL,

src/library/decision-context.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@
1515

1616
#include <stdbool.h>
1717
#include <stdatomic.h>
18+
#include <sys/types.h>
19+
#include <magic.h>
1820
#include "decision-defer.h"
1921
#include "lru.h"
2022
#include "message.h"
2123

24+
struct udev;
25+
26+
struct file_device_cache {
27+
dev_t device;
28+
char *devname;
29+
};
30+
2231
/*
2332
* decision_policy_counters - policy metrics updated by decision processing.
2433
*
@@ -62,6 +71,11 @@ struct decision_context {
6271
struct decision_defer_queue defer_queue;
6372
struct decision_defer_metrics last_defer_metrics;
6473
struct decision_policy_counters policy_counters;
74+
/* File helpers keep mutable parser/cache state private to a worker. */
75+
struct udev *file_udev;
76+
magic_t magic_fast;
77+
magic_t magic_full;
78+
struct file_device_cache device_cache;
6579
};
6680

6781
struct decision_context *decision_context_current(void);

src/library/file.c

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
#include "file.h"
4545
#include "database.h"
46+
#include "decision-context.h"
4647
#include "decision-timing.h"
4748
#include "paths.h"
4849
#include "message.h"
@@ -52,12 +53,6 @@
5253
// Local defines
5354
#define IMA_XATTR_DIGEST_NG 0x04 // security/integrity/integrity.h
5455

55-
// Local variables
56-
static struct udev *udev;
57-
magic_t magic_fast, magic_full;
58-
struct cache { dev_t device; const char *devname; };
59-
static struct cache c = { 0, NULL };
60-
6156
// Local declarations
6257
static ssize_t safe_read(int fd, char *buf, size_t size)
6358
__attr_access ((__write_only__, 2, 3));
@@ -66,6 +61,30 @@ static char *get_program_cwd_from_pid(pid_t pid, size_t blen, char *buf)
6661
static void resolve_path(const char *pcwd, char *path, size_t len)
6762
__attr_access ((__write_only__, 2, 3));
6863

64+
/*
65+
* file_close_context - release file helper state owned by a decision context.
66+
* @ctx: context whose libmagic, udev, and device cache state should be freed.
67+
* Returns nothing.
68+
*/
69+
static void file_close_context(struct decision_context *ctx)
70+
{
71+
if (ctx->file_udev) {
72+
udev_unref(ctx->file_udev);
73+
ctx->file_udev = NULL;
74+
}
75+
if (ctx->magic_fast) {
76+
magic_close(ctx->magic_fast);
77+
ctx->magic_fast = NULL;
78+
}
79+
if (ctx->magic_full) {
80+
magic_close(ctx->magic_full);
81+
ctx->magic_full = NULL;
82+
}
83+
free(ctx->device_cache.devname);
84+
ctx->device_cache.devname = NULL;
85+
ctx->device_cache.device = 0;
86+
}
87+
6988
// readelf -l path-to-app | grep 'Requesting' | cut -d':' -f2 | tr -d ' ]';
7089
static const char *interpreters[] = {
7190
"/lib64/ld-linux-x86-64.so.2",
@@ -95,13 +114,17 @@ static inline void rewind_fd(int fd)
95114
// Initialize what we can now so that its not done each call
96115
int file_init(void)
97116
{
117+
struct decision_context *ctx = decision_context_current();
118+
119+
file_close_context(ctx);
120+
98121
// Setup udev
99-
udev = udev_new();
122+
ctx->file_udev = udev_new();
100123

101124
// Setup libmagic
102125
unsetenv("MAGIC");
103126
// Fast magic: minimal rules, all expensive checks disabled
104-
magic_fast = magic_open(
127+
ctx->magic_fast = magic_open(
105128
MAGIC_MIME |
106129
MAGIC_ERROR |
107130
MAGIC_NO_CHECK_CDF |
@@ -112,31 +135,31 @@ int file_init(void)
112135
MAGIC_NO_CHECK_TOKENS | /* Skip text tokens */
113136
MAGIC_NO_CHECK_JSON /* Skip JSON validation */
114137
);
115-
if (magic_fast == NULL) {
138+
if (ctx->magic_fast == NULL) {
116139
msg(LOG_ERR, "Unable to init libmagic");
140+
file_close_context(ctx);
117141
return 1;
118142
}
119143

120144
// Load only essential magic rules
121-
if (magic_load(magic_fast, MAGIC_PATH) != 0) {
145+
if (magic_load(ctx->magic_fast, MAGIC_PATH) != 0) {
122146
msg(LOG_ERR, "Unable to load fast magic database");
123-
magic_close(magic_fast);
147+
file_close_context(ctx);
124148
return 2;
125149
}
126150

127151
// Full magic: normal operation
128-
magic_full = magic_open(MAGIC_MIME|MAGIC_ERROR|MAGIC_NO_CHECK_CDF|
152+
ctx->magic_full = magic_open(MAGIC_MIME|MAGIC_ERROR|MAGIC_NO_CHECK_CDF|
129153
MAGIC_NO_CHECK_ELF);
130-
if (magic_full == NULL) {
154+
if (ctx->magic_full == NULL) {
131155
msg(LOG_ERR, "Unable to init libmagic");
132-
magic_close(magic_fast);
156+
file_close_context(ctx);
133157
return 3;
134158
}
135159
// System default
136-
if (magic_load(magic_full, NULL) != 0) {
160+
if (magic_load(ctx->magic_full, NULL) != 0) {
137161
msg(LOG_ERR, "Unable to load default magic database");
138-
magic_close(magic_fast);
139-
magic_close(magic_full);
162+
file_close_context(ctx);
140163
return 4;
141164
}
142165
return 0;
@@ -146,10 +169,7 @@ int file_init(void)
146169
// Release memory during shutdown
147170
void file_close(void)
148171
{
149-
udev_unref(udev);
150-
magic_close(magic_fast);
151-
magic_close(magic_full);
152-
free((void *)c.devname);
172+
file_close_context(decision_context_current());
153173
}
154174

155175

@@ -587,19 +607,26 @@ char *get_file_from_fd(int fd, pid_t pid, size_t blen, char *buf)
587607

588608
char *get_device_from_stat(unsigned int device, size_t blen, char *buf)
589609
{
610+
struct decision_context *ctx = decision_context_current();
611+
struct file_device_cache *cache = &ctx->device_cache;
590612
struct udev_device *dev;
591613
const char *node;
592614

593-
if (c.device) {
594-
if (c.device == device) {
595-
strncpy(buf, c.devname, blen-1);
615+
if (cache->device) {
616+
if (cache->device == device) {
617+
strncpy(buf, cache->devname, blen-1);
596618
buf[blen-1] = 0;
597619
return buf;
598620
}
599621
}
600622

623+
if (ctx->file_udev == NULL)
624+
return NULL;
625+
601626
// Create udev_device from the dev_t obtained from stat
602-
dev = udev_device_new_from_devnum(udev, 'b', device);
627+
dev = udev_device_new_from_devnum(ctx->file_udev, 'b', device);
628+
if (dev == NULL)
629+
return NULL;
603630
node = udev_device_get_devnode(dev);
604631
if (node == NULL) {
605632
udev_device_unref(dev);
@@ -610,9 +637,12 @@ char *get_device_from_stat(unsigned int device, size_t blen, char *buf)
610637
udev_device_unref(dev);
611638

612639
// Update saved values
613-
free((void *)c.devname);
614-
c.device = device;
615-
c.devname = strdup(buf);
640+
free(cache->devname);
641+
cache->devname = strdup(buf);
642+
if (cache->devname)
643+
cache->device = device;
644+
else
645+
cache->device = 0;
616646

617647
return buf;
618648
}
@@ -1086,6 +1116,7 @@ const char *detect_text_format(const char *hdr, size_t len)
10861116
char *get_file_type_from_fd(int fd, const struct file_info *i, const char *path,
10871117
size_t blen, char *buf)
10881118
{
1119+
struct decision_context *ctx = decision_context_current();
10891120
const char *ptr;
10901121
char header[512 + 1];
10911122
size_t header_len = 0;
@@ -1184,15 +1215,18 @@ char *get_file_type_from_fd(int fd, const struct file_info *i, const char *path,
11841215
decision_timing_stage_end(&fast_timing);
11851216

11861217
// Use libmagic when in-house classification did not identify the object.
1218+
if (ctx->magic_fast == NULL || ctx->magic_full == NULL)
1219+
return NULL;
1220+
11871221
decision_timing_mime_stage_begin(
11881222
DECISION_TIMING_MIME_LIBMAGIC_FALLBACK, &timing);
1189-
ptr = magic_descriptor(magic_fast, fd);
1223+
ptr = magic_descriptor(ctx->magic_fast, fd);
11901224
if (ptr == NULL ||
11911225
(ptr && (memcmp(ptr, "text/plain", 10) == 0 ||
11921226
memcmp(ptr, "application/octet-stream", 24) == 0))) {
11931227
// Fall back to the whole database lookup
11941228
rewind_fd(fd);
1195-
ptr = magic_descriptor(magic_full, fd);
1229+
ptr = magic_descriptor(ctx->magic_full, fd);
11961230
if (ptr == NULL) {
11971231
decision_timing_stage_end(&timing);
11981232
return NULL;
@@ -1395,14 +1429,14 @@ int get_ima_hash(int fd, file_hash_alg_t *alg, char *sha)
13951429
}
13961430

13971431

1398-
static unsigned char e_ident[EI_NIDENT];
1399-
static inline ssize_t read_preliminary_header(int fd)
1432+
static inline ssize_t read_preliminary_header(int fd, unsigned char *e_ident)
14001433
{
14011434
return safe_read(fd, (char *)e_ident, EI_NIDENT);
14021435
}
14031436

14041437

1405-
static Elf32_Ehdr *read_header32(int fd, Elf32_Ehdr *ptr)
1438+
static Elf32_Ehdr *read_header32(int fd, Elf32_Ehdr *ptr,
1439+
const unsigned char *e_ident)
14061440
{
14071441
memcpy(ptr->e_ident, e_ident, EI_NIDENT);
14081442
ssize_t rc = safe_read(fd, (char *)ptr + EI_NIDENT,
@@ -1413,7 +1447,8 @@ static Elf32_Ehdr *read_header32(int fd, Elf32_Ehdr *ptr)
14131447
}
14141448

14151449

1416-
static Elf64_Ehdr *read_header64(int fd, Elf64_Ehdr *ptr)
1450+
static Elf64_Ehdr *read_header64(int fd, Elf64_Ehdr *ptr,
1451+
const unsigned char *e_ident)
14171452
{
14181453
memcpy(ptr->e_ident, e_ident, EI_NIDENT);
14191454
ssize_t rc = safe_read(fd, (char *)ptr + EI_NIDENT,
@@ -1496,10 +1531,11 @@ static int looks_like_text_script(int fd)
14961531
// size is the file size from fstat done when event was received
14971532
uint32_t gather_elf(int fd, off_t size)
14981533
{
1534+
unsigned char e_ident[EI_NIDENT];
14991535
uint32_t info = 0;
15001536
ssize_t rc;
15011537

1502-
rc = read_preliminary_header(fd);
1538+
rc = read_preliminary_header(fd, e_ident);
15031539
if (rc < 2)
15041540
goto rewind_out;
15051541

@@ -1527,7 +1563,7 @@ uint32_t gather_elf(int fd, off_t size)
15271563
Elf32_Phdr *ph_tbl = NULL;
15281564
Elf32_Ehdr hdr_buf;
15291565

1530-
Elf32_Ehdr *hdr = read_header32(fd, &hdr_buf);
1566+
Elf32_Ehdr *hdr = read_header32(fd, &hdr_buf, e_ident);
15311567
if (hdr == NULL) {
15321568
info |= HAS_ERROR;
15331569
goto rewind_out;
@@ -1682,7 +1718,7 @@ uint32_t gather_elf(int fd, off_t size)
16821718
Elf64_Phdr *ph_tbl;
16831719
Elf64_Ehdr hdr_buf;
16841720

1685-
Elf64_Ehdr *hdr = read_header64(fd, &hdr_buf);
1721+
Elf64_Ehdr *hdr = read_header64(fd, &hdr_buf, e_ident);
16861722
if (hdr == NULL) {
16871723
info |= HAS_ERROR;
16881724
goto rewind_out;

src/library/policy.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,8 @@ decision_t process_event(event_t *e)
933933
}
934934

935935
#ifdef FAN_AUDIT_RULE_NUM
936+
static int response_info_supported = 0;
937+
936938
static int test_info_api(int fd)
937939
{
938940
int rc;
@@ -955,17 +957,46 @@ static int test_info_api(int fd)
955957
}
956958
#endif
957959

960+
/*
961+
* reply_event_init - pre-probe extended fanotify response support.
962+
* @fd: fanotify listener fd used for permission responses.
963+
* Returns 0 on success or 1 when initialization cannot be attempted.
964+
*/
965+
int reply_event_init(int fd)
966+
{
967+
#ifdef FAN_AUDIT_RULE_NUM
968+
if (fd < 0) {
969+
errno = EBADF;
970+
msg(LOG_ERR,
971+
"Cannot initialize fanotify response info support: "
972+
"bad fd (%s)", strerror(errno));
973+
response_info_supported = 0;
974+
return 1;
975+
}
976+
977+
if (fcntl(fd, F_GETFD) < 0) {
978+
msg(LOG_ERR,
979+
"Cannot initialize fanotify response info support: "
980+
"bad fd (%s)", strerror(errno));
981+
response_info_supported = 0;
982+
return 1;
983+
}
984+
985+
response_info_supported = test_info_api(fd);
986+
#else
987+
(void)fd;
988+
#endif
989+
return 0;
990+
}
991+
958992
void reply_event(int fd, const struct fanotify_event_metadata *metadata,
959993
unsigned reply, event_t *e)
960994
{
961995
struct decision_timing_span prep_timing;
962996
struct decision_timing_span write_timing;
963997

964998
#ifdef FAN_AUDIT_RULE_NUM
965-
static int use_new = 2;
966-
if (use_new == 2)
967-
use_new = test_info_api(fd);
968-
if (reply & FAN_AUDIT && use_new) {
999+
if (reply & FAN_AUDIT && response_info_supported) {
9691000
struct fan_audit_response f;
9701001
subject_attr_t *sn;
9711002
object_attr_t *obj;

src/library/policy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void set_reload_rules(void);
9090
decision_t process_event(event_t *e);
9191
decision_t process_event_with_source(event_t *e, decision_source_t *source,
9292
struct decision_timing_span *response_timing);
93+
int reply_event_init(int fd);
9394
void reply_event(int fd, const struct fanotify_event_metadata *metadata,
9495
unsigned reply, event_t *e);
9596
void make_policy_decision(decision_event_t *decision_event, int fd,

0 commit comments

Comments
 (0)