mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 06:45:38 +00:00
Merge Make libaalogparse fully reentrant by removing its globals
Tested by using Valgrind's Helgrind and DRD against the reentrancy test that I wrote: they both report no errors with the changes while reporting many errors with the old versions. Commits "Inline _parse_yacc in libaalogparse" and "Make parse_record take a const char pointer since it never modified str anyways" have a tiny potential to be backwards-incompatible changes: I have justified why they shouldn't be in the commit messages, but it's worth looking over in case I was mistaken and we need to back those out. Signed-off-by: Ryan Lee <ryan.lee@canonical.com> MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1322 Approved-by: John Johansen <john@jjmx.net> Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -109,6 +109,10 @@ libraries/libapparmor/src/tst_aalogmisc
|
|||||||
libraries/libapparmor/src/tst_aalogmisc.log
|
libraries/libapparmor/src/tst_aalogmisc.log
|
||||||
libraries/libapparmor/src/tst_aalogmisc.o
|
libraries/libapparmor/src/tst_aalogmisc.o
|
||||||
libraries/libapparmor/src/tst_aalogmisc.trs
|
libraries/libapparmor/src/tst_aalogmisc.trs
|
||||||
|
libraries/libapparmor/src/tst_aalogparse_reentrancy
|
||||||
|
libraries/libapparmor/src/tst_aalogparse_reentrancy.log
|
||||||
|
libraries/libapparmor/src/tst_aalogparse_reentrancy.o
|
||||||
|
libraries/libapparmor/src/tst_aalogparse_reentrancy.trs
|
||||||
libraries/libapparmor/src/tst_features
|
libraries/libapparmor/src/tst_features
|
||||||
libraries/libapparmor/src/tst_features.log
|
libraries/libapparmor/src/tst_features.log
|
||||||
libraries/libapparmor/src/tst_features.o
|
libraries/libapparmor/src/tst_features.o
|
||||||
|
@@ -26,10 +26,10 @@
|
|||||||
#define AA_RECORD_LINK 16
|
#define AA_RECORD_LINK 16
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is just for convenience now that we have two
|
* Enum representing which syntax version the log entry used.
|
||||||
* wildly different grammars.
|
* Support for V1 parsing was completely removed in 2011 and that enum entry
|
||||||
|
* is only still there for API compatibility reasons.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
AA_RECORD_SYNTAX_V1,
|
AA_RECORD_SYNTAX_V1,
|
||||||
@@ -48,69 +48,6 @@ typedef enum
|
|||||||
AA_RECORD_STATUS /* Configuration change */
|
AA_RECORD_STATUS /* Configuration change */
|
||||||
} aa_record_event_type;
|
} aa_record_event_type;
|
||||||
|
|
||||||
/**
|
|
||||||
* With the sole exception of active_hat, this is a 1:1
|
|
||||||
* mapping from the keys that the new syntax uses.
|
|
||||||
*
|
|
||||||
* Some examples of the old syntax and how they're mapped with the aa_log_record struct:
|
|
||||||
*
|
|
||||||
* "PERMITTING r access to /path (program_name(12345) profile /profile active hat)"
|
|
||||||
* - operation: access
|
|
||||||
* - requested_mask: r
|
|
||||||
* - pid: 12345
|
|
||||||
* - profile: /profile
|
|
||||||
* - name: /path
|
|
||||||
* - info: program_name
|
|
||||||
* - active_hat: hat
|
|
||||||
*
|
|
||||||
* "REJECTING mkdir on /path/to/something (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out"
|
|
||||||
* - operation: mkdir
|
|
||||||
* - name: /path/to/something
|
|
||||||
* - info: bash
|
|
||||||
* - pid: 23415
|
|
||||||
* - profile: /bin/freak-aa-out
|
|
||||||
* - active_hat: /bin/freak-aa-out
|
|
||||||
*
|
|
||||||
* "REJECTING xattr set on /path/to/something (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
|
|
||||||
* - operation: xattr
|
|
||||||
* - attribute: set
|
|
||||||
* - name: /path/to/something
|
|
||||||
* - info: bash
|
|
||||||
* - pid: 23415
|
|
||||||
* - profile: /bin/freak-aa-out
|
|
||||||
* - active_hat: /bin/freak-aa-out
|
|
||||||
*
|
|
||||||
* "PERMITTING attribute (something) change to /else (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
|
|
||||||
* - operation: setattr
|
|
||||||
* - attribute: something
|
|
||||||
* - name: /else
|
|
||||||
* - info: bash
|
|
||||||
* - pid: 23415
|
|
||||||
* - profile: /bin/freak-aa-out
|
|
||||||
* - active_hat: /bin/freak-aa-out
|
|
||||||
*
|
|
||||||
* "PERMITTING access to capability 'cap' (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
|
|
||||||
* - operation: capability
|
|
||||||
* - name: cap
|
|
||||||
* - info: bash
|
|
||||||
* - pid: 23415
|
|
||||||
* - profile: /bin/freak-aa-out
|
|
||||||
* - active_hat: /bin/freak-aa-out
|
|
||||||
*
|
|
||||||
* "LOGPROF-HINT unknown_hat TESTHAT pid=27764 profile=/change_hat_test/test_hat active=/change_hat_test/test_hat"
|
|
||||||
* - operation: change_hat
|
|
||||||
* - name: TESTHAT
|
|
||||||
* - info: unknown_hat
|
|
||||||
* - pid: 27764
|
|
||||||
* - profile: /change_hat_test/test_hat
|
|
||||||
* - active_hat: /change_hat_test/test_hat
|
|
||||||
*
|
|
||||||
* "LOGPROF-HINT fork pid=27764 child=38229"
|
|
||||||
* - operation: clone
|
|
||||||
* - task: 38229
|
|
||||||
* - pid: 27764
|
|
||||||
**/
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
aa_record_syntax_version version;
|
aa_record_syntax_version version;
|
||||||
@@ -177,7 +114,7 @@ typedef struct
|
|||||||
* @return Parsed data.
|
* @return Parsed data.
|
||||||
*/
|
*/
|
||||||
aa_log_record *
|
aa_log_record *
|
||||||
parse_record(char *str);
|
parse_record(const char *str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees all struct data.
|
* Frees all struct data.
|
||||||
|
@@ -44,7 +44,7 @@ include $(COMMONDIR)/Make.rules
|
|||||||
|
|
||||||
BUILT_SOURCES = grammar.h scanner.h af_protos.h
|
BUILT_SOURCES = grammar.h scanner.h af_protos.h
|
||||||
AM_LFLAGS = -v
|
AM_LFLAGS = -v
|
||||||
AM_YFLAGS = -d -p aalogparse_
|
AM_YFLAGS = -Wno-yacc -d -p aalogparse_
|
||||||
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
|
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
|
||||||
scanner.h: scanner.l
|
scanner.h: scanner.l
|
||||||
$(LEX) -v $<
|
$(LEX) -v $<
|
||||||
@@ -73,6 +73,10 @@ CLEANFILES = libapparmor.pc
|
|||||||
tst_aalogmisc_SOURCES = tst_aalogmisc.c
|
tst_aalogmisc_SOURCES = tst_aalogmisc.c
|
||||||
tst_aalogmisc_LDADD = .libs/libapparmor.a
|
tst_aalogmisc_LDADD = .libs/libapparmor.a
|
||||||
|
|
||||||
|
tst_aalogparse_reentrancy_SOURCES = tst_aalogparse_reentrancy.c
|
||||||
|
tst_aalogparse_reentrancy_LDADD = .libs/libapparmor.a
|
||||||
|
tst_aalogparse_reentrancy_LDFLAGS = -pthread
|
||||||
|
|
||||||
tst_features_SOURCES = tst_features.c
|
tst_features_SOURCES = tst_features.c
|
||||||
tst_features_LDADD = .libs/libapparmor.a
|
tst_features_LDADD = .libs/libapparmor.a
|
||||||
|
|
||||||
@@ -80,7 +84,7 @@ tst_kernel_SOURCES = tst_kernel.c
|
|||||||
tst_kernel_LDADD = .libs/libapparmor.a
|
tst_kernel_LDADD = .libs/libapparmor.a
|
||||||
tst_kernel_LDFLAGS = -pthread
|
tst_kernel_LDFLAGS = -pthread
|
||||||
|
|
||||||
check_PROGRAMS = tst_aalogmisc tst_features tst_kernel
|
check_PROGRAMS = tst_aalogmisc tst_aalogparse_reentrancy tst_features tst_kernel
|
||||||
TESTS = $(check_PROGRAMS)
|
TESTS = $(check_PROGRAMS)
|
||||||
|
|
||||||
.PHONY: check-local
|
.PHONY: check-local
|
||||||
|
@@ -15,17 +15,15 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* aalogparse_error now requires visibility of the aa_log_record type
|
||||||
|
* Also include in a %code requires block to add it to the header
|
||||||
|
*/
|
||||||
|
%code requires{
|
||||||
|
#include <aalogparse.h>
|
||||||
|
}
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
||||||
/* set the following to non-zero to get bison to emit debugging
|
|
||||||
* information about tokens given and rules matched.
|
|
||||||
* Also:
|
|
||||||
* Uncomment the %defines
|
|
||||||
* parse.error
|
|
||||||
* parse.trace
|
|
||||||
*/
|
|
||||||
#define YYDEBUG 0
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <aalogparse.h>
|
#include <aalogparse.h>
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
@@ -41,12 +39,10 @@
|
|||||||
#define debug_unused_ unused_
|
#define debug_unused_ unused_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
aa_log_record *ret_record;
|
|
||||||
|
|
||||||
/* Since we're a library, on any errors we don't want to print out any
|
/* Since we're a library, on any errors we don't want to print out any
|
||||||
* error messages. We should probably add a debug interface that does
|
* error messages. We should probably add a debug interface that does
|
||||||
* emit messages when asked for. */
|
* emit messages when asked for. */
|
||||||
void aalogparse_error(unused_ void *scanner, debug_unused_ char const *s)
|
void aalogparse_error(unused_ void *scanner, aa_log_record *ret_record, debug_unused_ char const *s)
|
||||||
{
|
{
|
||||||
#if (YYDEBUG != 0)
|
#if (YYDEBUG != 0)
|
||||||
printf("ERROR: %s\n", s);
|
printf("ERROR: %s\n", s);
|
||||||
@@ -89,9 +85,10 @@ aa_record_event_type lookup_aa_event(unsigned int type)
|
|||||||
%define parse.trace
|
%define parse.trace
|
||||||
*/
|
*/
|
||||||
|
|
||||||
%define api.pure
|
%define api.pure full
|
||||||
%lex-param{void *scanner}
|
%lex-param{void *scanner}
|
||||||
%parse-param{void *scanner}
|
%parse-param{void *scanner}
|
||||||
|
%parse-param{aa_log_record *ret_record}
|
||||||
|
|
||||||
%union
|
%union
|
||||||
{
|
{
|
||||||
@@ -284,8 +281,9 @@ audit_user_msg: TOK_KEY_MSG TOK_EQUALS audit_id audit_user_msg_tail
|
|||||||
|
|
||||||
audit_id: TOK_AUDIT TOK_OPEN_PAREN TOK_AUDIT_DIGITS TOK_PERIOD TOK_AUDIT_DIGITS TOK_COLON TOK_AUDIT_DIGITS TOK_CLOSE_PAREN TOK_COLON
|
audit_id: TOK_AUDIT TOK_OPEN_PAREN TOK_AUDIT_DIGITS TOK_PERIOD TOK_AUDIT_DIGITS TOK_COLON TOK_AUDIT_DIGITS TOK_CLOSE_PAREN TOK_COLON
|
||||||
{
|
{
|
||||||
if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7))
|
if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7)) {
|
||||||
yyerror(scanner, YY_("Out of memory"));
|
yyerror(scanner, ret_record, YY_("Out of memory"));
|
||||||
|
}
|
||||||
ret_record->epoch = atol($3);
|
ret_record->epoch = atol($3);
|
||||||
ret_record->audit_sub_id = atoi($7);
|
ret_record->audit_sub_id = atoi($7);
|
||||||
free($3);
|
free($3);
|
||||||
@@ -477,31 +475,3 @@ protocol: TOK_QUOTED_STRING
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
%%
|
%%
|
||||||
|
|
||||||
aa_log_record *
|
|
||||||
_parse_yacc(char *str)
|
|
||||||
{
|
|
||||||
/* yydebug = 1; */
|
|
||||||
YY_BUFFER_STATE lex_buf;
|
|
||||||
yyscan_t scanner;
|
|
||||||
|
|
||||||
ret_record = NULL;
|
|
||||||
ret_record = malloc(sizeof(aa_log_record));
|
|
||||||
|
|
||||||
_init_log_record(ret_record);
|
|
||||||
|
|
||||||
if (ret_record == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
#if (YYDEBUG != 0)
|
|
||||||
yydebug = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
aalogparse_lex_init(&scanner);
|
|
||||||
lex_buf = aalogparse__scan_string(str, scanner);
|
|
||||||
/* Ignore return value to return an AA_RECORD_INVALID event */
|
|
||||||
(void)aalogparse_parse(scanner);
|
|
||||||
aalogparse__delete_buffer(lex_buf, scanner);
|
|
||||||
aalogparse_lex_destroy(scanner);
|
|
||||||
return ret_record;
|
|
||||||
}
|
|
||||||
|
@@ -34,13 +34,42 @@
|
|||||||
#include <aalogparse.h>
|
#include <aalogparse.h>
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include "grammar.h"
|
||||||
|
#include "scanner.h"
|
||||||
|
|
||||||
/* This is mostly just a wrapper around the code in grammar.y */
|
/* This is mostly just a wrapper around the code in grammar.y */
|
||||||
aa_log_record *parse_record(char *str)
|
aa_log_record *parse_record(const char *str)
|
||||||
{
|
{
|
||||||
|
YY_BUFFER_STATE lex_buf;
|
||||||
|
yyscan_t scanner;
|
||||||
|
aa_log_record *ret_record;
|
||||||
|
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return _parse_yacc(str);
|
ret_record = malloc(sizeof(aa_log_record));
|
||||||
|
|
||||||
|
_init_log_record(ret_record);
|
||||||
|
|
||||||
|
if (ret_record == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct string_buf string_buf = {.buf = NULL, .buf_len = 0, .buf_alloc = 0};
|
||||||
|
|
||||||
|
#if (YYDEBUG != 0)
|
||||||
|
/* Warning: this is still a global even in reentrant parsers */
|
||||||
|
aalogparse_debug = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
aalogparse_lex_init_extra(&string_buf, &scanner);
|
||||||
|
lex_buf = aalogparse__scan_string(str, scanner);
|
||||||
|
/* Ignore return value to return an AA_RECORD_INVALID event */
|
||||||
|
(void)aalogparse_parse(scanner, ret_record);
|
||||||
|
aalogparse__delete_buffer(lex_buf, scanner);
|
||||||
|
aalogparse_lex_destroy(scanner);
|
||||||
|
/* free(NULL) is a no-op */
|
||||||
|
free(string_buf.buf);
|
||||||
|
return ret_record;
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_record(aa_log_record *record)
|
void free_record(aa_log_record *record)
|
||||||
|
@@ -19,8 +19,14 @@
|
|||||||
#ifndef __AA_LOG_PARSER_H__
|
#ifndef __AA_LOG_PARSER_H__
|
||||||
#define __AA_LOG_PARSER_H__
|
#define __AA_LOG_PARSER_H__
|
||||||
|
|
||||||
|
// Internal-only type
|
||||||
|
struct string_buf {
|
||||||
|
char *buf;
|
||||||
|
unsigned int buf_len;
|
||||||
|
unsigned int buf_alloc;
|
||||||
|
};
|
||||||
|
|
||||||
extern void _init_log_record(aa_log_record *record);
|
extern void _init_log_record(aa_log_record *record);
|
||||||
extern aa_log_record *_parse_yacc(char *str);
|
|
||||||
extern char *hex_to_string(char *str);
|
extern char *hex_to_string(char *str);
|
||||||
extern char *ipproto_to_string(unsigned int proto);
|
extern char *ipproto_to_string(unsigned int proto);
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
%option nounput
|
%option nounput
|
||||||
%option noyy_top_state
|
%option noyy_top_state
|
||||||
%option reentrant
|
%option reentrant
|
||||||
|
%option extra-type="struct string_buf*"
|
||||||
%option prefix="aalogparse_"
|
%option prefix="aalogparse_"
|
||||||
%option bison-bridge
|
%option bison-bridge
|
||||||
%option header-file="scanner.h"
|
%option header-file="scanner.h"
|
||||||
@@ -34,40 +35,37 @@
|
|||||||
|
|
||||||
#define YY_NO_INPUT
|
#define YY_NO_INPUT
|
||||||
|
|
||||||
unsigned int string_buf_alloc = 0;
|
void string_buf_reset(struct string_buf* char_buf)
|
||||||
unsigned int string_buf_len = 0;
|
|
||||||
char *string_buf = NULL;
|
|
||||||
|
|
||||||
void string_buf_reset()
|
|
||||||
{
|
{
|
||||||
/* rewind buffer to zero, possibly doing initial allocation too */
|
/* rewind buffer to zero, possibly doing initial allocation too */
|
||||||
string_buf_len = 0;
|
char_buf->buf_len = 0;
|
||||||
if (string_buf == NULL) {
|
if (char_buf->buf == NULL) {
|
||||||
string_buf_alloc = 128;
|
char_buf->buf_alloc = 128;
|
||||||
string_buf = malloc(string_buf_alloc);
|
char_buf->buf = malloc(char_buf->buf_alloc);
|
||||||
assert(string_buf != NULL);
|
assert(char_buf->buf != NULL);
|
||||||
}
|
}
|
||||||
/* always start with a valid but empty string */
|
/* always start with a valid but empty string */
|
||||||
string_buf[0] = '\0';
|
char_buf->buf[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void string_buf_append(unsigned int length, char *text)
|
void string_buf_append(struct string_buf* char_buf, unsigned int length, char *text)
|
||||||
{
|
{
|
||||||
unsigned int current_length = string_buf_len;
|
unsigned int current_length = char_buf->buf_len;
|
||||||
|
|
||||||
/* handle calling ..._append before ..._reset */
|
/* handle calling ..._append before ..._reset */
|
||||||
if (string_buf == NULL) string_buf_reset();
|
if (char_buf->buf == NULL) string_buf_reset(char_buf);
|
||||||
|
|
||||||
string_buf_len += length;
|
char_buf->buf_len += length;
|
||||||
/* expand allocation if this append would exceed the allocation */
|
/* expand allocation if this append would exceed the allocation */
|
||||||
while (string_buf_len >= string_buf_alloc) {
|
while (char_buf->buf_len >= char_buf->buf_alloc) {
|
||||||
string_buf_alloc *= 2;
|
// TODO: overflow?
|
||||||
string_buf = realloc(string_buf, string_buf_alloc);
|
char_buf->buf_alloc *= 2;
|
||||||
assert(string_buf != NULL);
|
char_buf->buf = realloc(char_buf->buf, char_buf->buf_alloc);
|
||||||
|
assert(char_buf->buf != NULL);
|
||||||
}
|
}
|
||||||
/* copy and unconditionally terminate */
|
/* copy and unconditionally terminate */
|
||||||
memcpy(string_buf+current_length, text, length);
|
memcpy(char_buf->buf+current_length, text, length);
|
||||||
string_buf[string_buf_len] = '\0';
|
char_buf->buf[char_buf->buf_len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
@@ -232,7 +230,7 @@ yy_flex_debug = 0;
|
|||||||
{open_paren} { return(TOK_OPEN_PAREN); }
|
{open_paren} { return(TOK_OPEN_PAREN); }
|
||||||
{close_paren} { BEGIN(INITIAL); return(TOK_CLOSE_PAREN); }
|
{close_paren} { BEGIN(INITIAL); return(TOK_CLOSE_PAREN); }
|
||||||
{ws} { }
|
{ws} { }
|
||||||
\" { string_buf_reset(); BEGIN(quoted_string); }
|
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
|
||||||
{ID}+ {
|
{ID}+ {
|
||||||
yylval->t_str = strdup(yytext);
|
yylval->t_str = strdup(yytext);
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
@@ -241,20 +239,20 @@ yy_flex_debug = 0;
|
|||||||
{equals} { return(TOK_EQUALS); }
|
{equals} { return(TOK_EQUALS); }
|
||||||
}
|
}
|
||||||
|
|
||||||
\" { string_buf_reset(); BEGIN(quoted_string); }
|
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
|
||||||
<quoted_string>\" { /* End of the quoted string */
|
<quoted_string>\" { /* End of the quoted string */
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
yylval->t_str = strdup(string_buf);
|
yylval->t_str = strdup(yyextra->buf);
|
||||||
return(TOK_QUOTED_STRING);
|
return(TOK_QUOTED_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<quoted_string>\\(.|\n) { string_buf_append(1, &yytext[1]); }
|
<quoted_string>\\(.|\n) { string_buf_append(yyextra, 1, &yytext[1]); }
|
||||||
|
|
||||||
<quoted_string>[^\\\n\"]+ { string_buf_append(yyleng, yytext); }
|
<quoted_string>[^\\\n\"]+ { string_buf_append(yyextra, yyleng, yytext); }
|
||||||
|
|
||||||
<safe_string>{
|
<safe_string>{
|
||||||
\" { string_buf_reset(); BEGIN(quoted_string); }
|
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
|
||||||
{hexstring} { yylval->t_str = hex_to_string(yytext); BEGIN(INITIAL); return(TOK_HEXSTRING);}
|
{hexstring} { yylval->t_str = hex_to_string(yytext); BEGIN(INITIAL); return(TOK_HEXSTRING);}
|
||||||
{equals} { return(TOK_EQUALS); }
|
{equals} { return(TOK_EQUALS); }
|
||||||
. { /* eek, error! try another state */ BEGIN(INITIAL); yyless(0); }
|
. { /* eek, error! try another state */ BEGIN(INITIAL); yyless(0); }
|
||||||
|
154
libraries/libapparmor/src/tst_aalogparse_reentrancy.c
Normal file
154
libraries/libapparmor/src/tst_aalogparse_reentrancy.c
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <aalogparse.h>
|
||||||
|
|
||||||
|
#include "private.h"
|
||||||
|
|
||||||
|
const char* log_line = "[23342.075380] audit: type=1400 audit(1725487203.971:1831): apparmor=\"DENIED\" operation=\"open\" class=\"file\" profile=\"snap-update-ns.firmware-updater\" name=\"/proc/202964/maps\" pid=202964 comm=\"5\" requested_mask=\"r\" denied_mask=\"r\" fsuid=1000 ouid=0";
|
||||||
|
const char* log_line_2 = "[ 4074.372559] audit: type=1400 audit(1725553393.143:793): apparmor=\"DENIED\" operation=\"capable\" class=\"cap\" profile=\"/usr/lib/snapd/snap-confine\" pid=19034 comm=\"snap-confine\" capability=12 capname=\"net_admin\"";
|
||||||
|
|
||||||
|
static int pthread_barrier_ok(int barrier_result) {
|
||||||
|
return barrier_result == 0 || barrier_result == PTHREAD_BARRIER_SERIAL_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nullcmp_and_strcmp(const void *s1, const void *s2)
|
||||||
|
{
|
||||||
|
/* Return 0 if both pointers are NULL & non-zero if only one is NULL */
|
||||||
|
if (!s1 || !s2)
|
||||||
|
return s1 != s2;
|
||||||
|
|
||||||
|
return strcmp(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int aa_log_record_eq(aa_log_record *record1, aa_log_record *record2) {
|
||||||
|
int are_eq = 1;
|
||||||
|
|
||||||
|
are_eq &= (record1->version == record2->version);
|
||||||
|
are_eq &= (record1->event == record2->event);
|
||||||
|
are_eq &= (record1->pid == record2->pid);
|
||||||
|
are_eq &= (record1->peer_pid == record2->peer_pid);
|
||||||
|
are_eq &= (record1->task == record2->task);
|
||||||
|
are_eq &= (record1->magic_token == record2->magic_token);
|
||||||
|
are_eq &= (record1->epoch == record2->epoch);
|
||||||
|
are_eq &= (record1->audit_sub_id == record2->audit_sub_id);
|
||||||
|
|
||||||
|
are_eq &= (record1->bitmask == record2->bitmask);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->audit_id, record2->audit_id) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->operation, record2->operation) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->denied_mask, record2->denied_mask) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->requested_mask, record2->requested_mask) == 0);
|
||||||
|
are_eq &= (record1->fsuid == record2->fsuid);
|
||||||
|
are_eq &= (record1->ouid == record2->ouid);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->profile, record2->profile) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->peer_profile, record2->peer_profile) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->comm, record2->comm) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->name, record2->name) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->name2, record2->name2) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->namespace, record2->namespace) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->attribute, record2->attribute) == 0);
|
||||||
|
are_eq &= (record1->parent == record2->parent);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->info, record2->info) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->peer_info, record2->peer_info) == 0);
|
||||||
|
are_eq &= (record1->error_code == record2->error_code);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->active_hat, record2->active_hat) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_family, record2->net_family) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_protocol, record2->net_protocol) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_sock_type, record2->net_sock_type) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_local_addr, record2->net_local_addr) == 0);
|
||||||
|
are_eq &= (record1->net_local_port == record2->net_local_port);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_foreign_addr, record2->net_foreign_addr) == 0);
|
||||||
|
are_eq &= (record1->net_foreign_port == record2->net_foreign_port);
|
||||||
|
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->execpath, record2->execpath) == 0);
|
||||||
|
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->dbus_bus, record2->dbus_bus) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->dbus_path, record2->dbus_path) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->dbus_interface, record2->dbus_interface) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->dbus_member, record2->dbus_member) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->signal, record2->signal) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->peer, record2->peer) == 0);
|
||||||
|
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->fs_type, record2->fs_type) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->flags, record2->flags) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->src_name, record2->src_name) == 0);
|
||||||
|
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->class, record2->class) == 0);
|
||||||
|
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->net_addr, record2->net_addr) == 0);
|
||||||
|
are_eq &= (nullcmp_and_strcmp(record1->peer_addr, record2->peer_addr) == 0);
|
||||||
|
return are_eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* log;
|
||||||
|
pthread_barrier_t *barrier;
|
||||||
|
} pthread_parse_args;
|
||||||
|
|
||||||
|
void* pthread_parse_log(void* args) {
|
||||||
|
pthread_parse_args *args_real = (pthread_parse_args *) args;
|
||||||
|
int barrier_wait_result = pthread_barrier_wait(args_real->barrier);
|
||||||
|
/* Return NULL and fail test if barrier wait fails */
|
||||||
|
if (!pthread_barrier_ok(barrier_wait_result)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
aa_log_record *record = parse_record(args_real->log);
|
||||||
|
return (void*) record;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_THREADS 16
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
pthread_t thread_ids[NUM_THREADS];
|
||||||
|
pthread_barrier_t barrier;
|
||||||
|
int barrier_wait_result;
|
||||||
|
aa_log_record* parsed_logs[NUM_THREADS];
|
||||||
|
int rc = 0;
|
||||||
|
/* Set up arguments to be passed to threads */
|
||||||
|
pthread_parse_args args = {.log=log_line, .barrier=&barrier};
|
||||||
|
pthread_parse_args args2 = {.log=log_line_2, .barrier=&barrier};
|
||||||
|
|
||||||
|
MY_TEST(NUM_THREADS > 2, "Test requires more than 2 threads");
|
||||||
|
|
||||||
|
/* Use barrier to synchronize the start of log parsing among all the threads
|
||||||
|
* This increases the likelihood of tickling race conditions, if there are any
|
||||||
|
*/
|
||||||
|
MY_TEST(pthread_barrier_init(&barrier, NULL, NUM_THREADS+1) == 0,
|
||||||
|
"Could not init pthread barrier");
|
||||||
|
for (int i=0; i<NUM_THREADS; i++) {
|
||||||
|
if (i%2 == 0) {
|
||||||
|
pthread_create(&thread_ids[i], NULL, pthread_parse_log, (void *) &args);
|
||||||
|
} else {
|
||||||
|
pthread_create(&thread_ids[i], NULL, pthread_parse_log, (void *) &args2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Final barrier_wait to set off the thread race */
|
||||||
|
barrier_wait_result = pthread_barrier_wait(&barrier);
|
||||||
|
MY_TEST(pthread_barrier_ok(barrier_wait_result), "Could not wait on pthread barrier");
|
||||||
|
|
||||||
|
/* Wait for threads to finish parsing the logs */
|
||||||
|
for (int i=0; i<NUM_THREADS; i++) {
|
||||||
|
MY_TEST(pthread_join(thread_ids[i], (void*) &parsed_logs[i]) == 0, "Could not join thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that all logs parsed and are equal */
|
||||||
|
for (int i=0; i<NUM_THREADS; i++) {
|
||||||
|
MY_TEST(parsed_logs[i] != NULL, "Log failed to parse");
|
||||||
|
MY_TEST(parsed_logs[i]->version == AA_RECORD_SYNTAX_V2, "Log should have parsed as v2 form");
|
||||||
|
MY_TEST(parsed_logs[i]->event == AA_RECORD_DENIED, "Log should have parsed as denied");
|
||||||
|
|
||||||
|
/* Also check i==0 and i==1 as a sanity check for aa_log_record_eq */
|
||||||
|
if (i%2 == 0) {
|
||||||
|
MY_TEST(aa_log_record_eq(parsed_logs[0], parsed_logs[i]), "Log 0 != Log even");
|
||||||
|
} else {
|
||||||
|
MY_TEST(aa_log_record_eq(parsed_logs[1], parsed_logs[i]), "Log 1 != Log odd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MY_TEST(!aa_log_record_eq(parsed_logs[0], parsed_logs[1]), "Log 0 and log 1 shouldn't be equal");
|
||||||
|
/* Clean up */
|
||||||
|
MY_TEST(pthread_barrier_destroy(&barrier) == 0, "Could not destroy pthread barrier");
|
||||||
|
for (int i=0; i<NUM_THREADS; i++) {
|
||||||
|
free_record(parsed_logs[i]);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
Reference in New Issue
Block a user