Skip to content

Commit ba5ed39

Browse files
committed
Reduce memory churn in netlink_handler
auditd allocates a new struct auditd_event each time the netlink handler (netlink_handler) reads a record. The handler keeps a single pointer (cur_event) to the currently processed record. When the optional configuration worker finishes, it sends the event back via reconfig_ev and the pipe handler frees it. Thus only one event structure is normally active in the netlink handler. A second structure exists only temporarily during reconfiguration (held in reconfig_ev). Once an event is processed, distribute_event() copies it into a new event_t structure for the dispatcher queue. The dispatcher queue (audisp/queue.c) can hold up to the configured depth (default 2000). Each queued entry is freed after all plugins process it. The dispatch thread later frees the record. Only one (occasionally two) auditd_event objects exist at any given time. Preallocating a small static instance (or a pool of two) would remove nearly all per‑event allocations without additional complexity.
1 parent 871b8a4 commit ba5ed39

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

src/auditd-event.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ extern volatile ATOMIC_INT stop;
5454
#endif
5555

5656
extern void update_report_timer(unsigned int interval);
57+
/*
58+
* This function is provided by auditd.c and marked weak so test utilities
59+
* that don't link auditd.c can still link this file.
60+
*/
61+
extern int event_is_prealloc(struct auditd_event *e) __attribute__((weak));
5762

5863
/* Local function prototypes */
5964
static void send_ack(const struct auditd_event *e, int ack_type,
@@ -580,7 +585,8 @@ void cleanup_event(struct auditd_event *e)
580585
// into the middle of the reply allocation. Check for it.
581586
if (e->reply.message != e->reply.msg.data)
582587
free((void *)e->reply.message);
583-
free(e);
588+
if (!event_is_prealloc || !event_is_prealloc(e))
589+
free(e);
584590
}
585591

586592
/* This function takes a reconfig event and sends it to the handler */

src/auditd.c

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ static const char *pidfile = "/var/run/auditd.pid";
8282
static const char *state_file = "/var/run/auditd.state";
8383
static int init_pipe[2];
8484
static int do_fork = 1, opt_aggregate_only = 0, config_dir_set = 0;
85+
/*
86+
* Small pool of reusable event structures. The netlink handler needs
87+
* one event while processing the current record and a second for
88+
* reconfigure work. Keeping them here avoids repeated allocations.
89+
*/
90+
struct auditd_event event_pool[2];
8591
static struct auditd_event *cur_event = NULL, *reconfig_ev = NULL;
8692
static ATOMIC_INT hup_info_requested = 0;
8793
static ATOMIC_INT usr1_info_requested = 0, usr2_info_requested = 0;
@@ -95,6 +101,9 @@ int send_audit_event(int type, const char *str);
95101
static void clean_exit(void);
96102
static int get_reply(int fd, struct audit_reply *rep, int seq);
97103
static char *getsubj(char *subj);
104+
/* Manage access to the preallocated event pool */
105+
static struct auditd_event *alloc_pool_event(void);
106+
int event_is_prealloc(struct auditd_event *e);
98107

99108
enum startup_state {startup_disable=0, startup_enable, startup_nochange,
100109
startup_INVALID};
@@ -536,6 +545,30 @@ static void tell_parent(int status)
536545
} while (rc < 0 && errno == EINTR);
537546
}
538547

548+
/*
549+
* alloc_pool_event - return an unused entry from event_pool
550+
*
551+
* The pool holds two structures so the netlink handler and the
552+
* configuration worker can each have one. NULL is returned if both
553+
* are already in use.
554+
*/
555+
static struct auditd_event *alloc_pool_event(void)
556+
{
557+
if (cur_event != &event_pool[0] && reconfig_ev != &event_pool[0])
558+
return &event_pool[0];
559+
if (cur_event != &event_pool[1] && reconfig_ev != &event_pool[1])
560+
return &event_pool[1];
561+
return NULL;
562+
}
563+
564+
/*
565+
* event_is_prealloc - true if 'e' points into event_pool
566+
*/
567+
int event_is_prealloc(struct auditd_event *e)
568+
{
569+
return e == &event_pool[0] || e == &event_pool[1];
570+
}
571+
539572
static void netlink_handler(struct ev_loop *loop, struct ev_io *io,
540573
int revents)
541574
{
@@ -546,7 +579,8 @@ static void netlink_handler(struct ev_loop *loop, struct ev_io *io,
546579
// FIXME: backing down to 3 until IPC is faster
547580
while (rc > 0 && cnt < 3) {
548581
if (cur_event == NULL) {
549-
if ((cur_event = malloc(sizeof(*cur_event))) == NULL) {
582+
cur_event = alloc_pool_event();
583+
if (cur_event == NULL) {
550584
char emsg[DEFAULT_BUF_SZ];
551585
if (*subj)
552586
snprintf(emsg, sizeof(emsg),
@@ -1079,9 +1113,8 @@ int main(int argc, char *argv[])
10791113
}
10801114
}
10811115
if (rc <= 0)
1082-
send_audit_event(AUDIT_DAEMON_END,
1116+
send_audit_event(AUDIT_DAEMON_END,
10831117
"op=terminate auid=-1 uid=-1 ses=-1 pid=-1 subj=? res=success");
1084-
free(cur_event);
10851118

10861119
// Tear down IO watchers Part 2
10871120
if (!opt_aggregate_only)

0 commit comments

Comments
 (0)