Skip to content

Commit 72b0132

Browse files
pcmooredrakenclimber
authored andcommitted
api: add transaction support to the libseccomp API
While libseccomp has internally has transaction support for some time now, it hasn't been accessible to callers through the libseccomp API. This patch adds a transaction API as well as supporting documentation and a new unit regression test. int seccomp_transaction_start(const scmp_filter_ctx ctx) int seccomp_transaction_commit(const scmp_filter_ctx ctx) void seccomp_transaction_reject(const scmp_filter_ctx ctx) Signed-off-by: Paul Moore <[email protected]> Signed-off-by: Tom Hromatka <[email protected]>
1 parent 557a788 commit 72b0132

File tree

12 files changed

+503
-12
lines changed

12 files changed

+503
-12
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
.TH "seccomp_transaction_start" 3 "21 September 2023" "[email protected]" "libseccomp Documentation"
2+
.\" //////////////////////////////////////////////////////////////////////////
3+
.SH NAME
4+
.\" //////////////////////////////////////////////////////////////////////////
5+
seccomp_transaction_start, seccomp_transaction_commit, seccomp_transaction_reject \- Manage seccomp filter transactions
6+
.\" //////////////////////////////////////////////////////////////////////////
7+
.SH SYNOPSIS
8+
.\" //////////////////////////////////////////////////////////////////////////
9+
.nf
10+
.B #include <seccomp.h>
11+
.sp
12+
.B typedef void * scmp_filter_ctx;
13+
.sp
14+
.BI "int seccomp_transaction_start(scmp_filter_ctx " ctx ");
15+
.BI "int seccomp_transaction_commit(scmp_filter_ctx " ctx ");
16+
.BI "void seccomp_transaction_reject(scmp_filter_ctx " ctx ");
17+
.sp
18+
Link with \fI\-lseccomp\fP.
19+
.fi
20+
.\" //////////////////////////////////////////////////////////////////////////
21+
.SH DESCRIPTION
22+
.\" //////////////////////////////////////////////////////////////////////////
23+
.P
24+
The
25+
.BR seccomp_transaction_start ()
26+
function starts a new seccomp filter
27+
transaction that the caller can use to perform any number of filter
28+
modifications which can then be committed to the filter using
29+
.BR seccomp_transaction_commit ()
30+
or rejected using
31+
.BR seccomp_transaction_reject ().
32+
It is important to note that transactions only affect the seccomp filter state
33+
while it is being managed by libseccomp; seccomp filters which have been loaded
34+
into the kernel can not be modified, only new seccomp filters can be added on
35+
top of the existing loaded filter stack.
36+
.P
37+
Finishing, or committing, a transaction is optional, although it is encouraged.
38+
At any point in time, regardless of the transaction state, the seccomp filter
39+
is determined by all of the libseccomp operations performed on the filter up to
40+
that point. Committing a transaction simply flushes the transaction rollback
41+
marker of the current transaction making the filter changes permanent;
42+
rejecting a transaction rolls the filter state back to immediately before the
43+
transaction was started.
44+
.P
45+
Transactions can be nested arbitrarily deep with the
46+
.BR seccomp_transaction_commit ()
47+
and
48+
.BR seccomp_transaction_reject ()
49+
functions always operating on the deepest, or more recently started transaction.
50+
A nested set of filter modifications, even if committed, is still subject to
51+
rejection by shallower, or older transactions that have yet to be committed or
52+
rejected.
53+
.\" //////////////////////////////////////////////////////////////////////////
54+
.SH RETURN VALUE
55+
.\" //////////////////////////////////////////////////////////////////////////
56+
The
57+
.BR seccomp_transaction_start ()
58+
and
59+
.BR seccomp_transaction_commit ()
60+
functions return zero on success or one of the following error codes on
61+
failure:
62+
.TP
63+
.B -ENOMEM
64+
The library was unable to allocate enough memory.
65+
.\" //////////////////////////////////////////////////////////////////////////
66+
.SH EXAMPLES
67+
.\" //////////////////////////////////////////////////////////////////////////
68+
.nf
69+
#include <seccomp.h>
70+
71+
int libseccomp_generate(scmp_filter_ctx *ctx)
72+
{
73+
int rc;
74+
75+
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
76+
if (rc)
77+
return rc;
78+
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
79+
if (rc)
80+
return rc;
81+
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
82+
if (rc)
83+
return rc;
84+
85+
return 0;
86+
}
87+
88+
int main(int argc, char *argv[])
89+
{
90+
int rc = \-1;
91+
scmp_filter_ctx ctx;
92+
93+
ctx = seccomp_init(SCMP_ACT_KILL);
94+
if (ctx == NULL)
95+
goto out;
96+
97+
rc = seccomp_transaction_start(ctx)
98+
if (rc)
99+
goto out;
100+
rc = libseccomp_generate(ctx);
101+
if (rc == 0) {
102+
rc = seccomp_transaction_commit(ctx);
103+
if (rc)
104+
goto out;
105+
} else
106+
seccomp_transaction_reject(ctx);
107+
108+
/* ... */
109+
110+
out:
111+
seccomp_release(ctx);
112+
return \-rc;
113+
}
114+
.fi
115+
.\" //////////////////////////////////////////////////////////////////////////
116+
.SH NOTES
117+
.\" //////////////////////////////////////////////////////////////////////////
118+
.P
119+
While the seccomp filter can be generated independent of the kernel, kernel
120+
support is required to load and enforce the seccomp filter generated by
121+
libseccomp.
122+
.P
123+
The libseccomp project site, with more information and the source code
124+
repository, can be found at https://github.com/seccomp/libseccomp. This tool,
125+
as well as the libseccomp library, is currently under development, please
126+
report any bugs at the project site or directly to the author.
127+
.\" //////////////////////////////////////////////////////////////////////////
128+
.SH AUTHOR
129+
.\" //////////////////////////////////////////////////////////////////////////
130+
Paul Moore <[email protected]>
131+
.\" //////////////////////////////////////////////////////////////////////////
132+
.SH SEE ALSO
133+
.\" //////////////////////////////////////////////////////////////////////////
134+
.BR seccomp_init (3),

include/seccomp.h.in

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,35 @@ int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd);
849849
*/
850850
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf, size_t *len);
851851

852+
/**
853+
* Start a filter transaction
854+
* @param ctx the filter context
855+
*
856+
* This function starts a filter transaction for modifying the seccomp filter.
857+
* Returns zero on success, negative values on failure.
858+
*
859+
*/
860+
int seccomp_transaction_start(const scmp_filter_ctx ctx);
861+
862+
/**
863+
* Reject the current filter transaction
864+
* @param ctx the filter context
865+
*
866+
* This function rejects the current seccomp filter transaction.
867+
*
868+
*/
869+
void seccomp_transaction_reject(const scmp_filter_ctx ctx);
870+
871+
/**
872+
* Commit the current filter transaction
873+
* @param ctx the filter context
874+
*
875+
* This function commits the current seccomp filter transaction. Returns zero
876+
* on success, negative values on failure.
877+
*
878+
*/
879+
int seccomp_transaction_commit(const scmp_filter_ctx ctx);
880+
852881
/**
853882
* Precompute the seccomp filter for future use
854883
* @param ctx the filter context

src/api.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,45 @@ API int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
793793
return rc;
794794
}
795795

796+
/* NOTE - function header comment in include/seccomp.h */
797+
API int seccomp_transaction_start(const scmp_filter_ctx ctx)
798+
{
799+
int rc;
800+
struct db_filter_col *col;
801+
802+
if (_ctx_valid(ctx))
803+
return _rc_filter(-EINVAL);
804+
col = (struct db_filter_col *)ctx;
805+
806+
rc = db_col_transaction_start(col, true);
807+
return _rc_filter(rc);
808+
}
809+
810+
/* NOTE - function header comment in include/seccomp.h */
811+
API void seccomp_transaction_reject(const scmp_filter_ctx ctx)
812+
{
813+
struct db_filter_col *col;
814+
815+
if (_ctx_valid(ctx))
816+
return;
817+
col = (struct db_filter_col *)ctx;
818+
819+
db_col_transaction_abort(col, true);
820+
}
821+
822+
/* NOTE - function header comment in include/seccomp.h */
823+
API int seccomp_transaction_commit(const scmp_filter_ctx ctx)
824+
{
825+
struct db_filter_col *col;
826+
827+
if (_ctx_valid(ctx))
828+
return _rc_filter(-EINVAL);
829+
col = (struct db_filter_col *)ctx;
830+
831+
db_col_transaction_commit(col, true);
832+
return _rc_filter(0);
833+
}
834+
796835
/* NOTE - function header comment in include/seccomp.h */
797836
API int seccomp_precompute(const scmp_filter_ctx ctx)
798837
{

src/db.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,7 +2369,7 @@ int db_col_rule_add(struct db_filter_col *col,
23692369
}
23702370

23712371
/* create a checkpoint */
2372-
rc = db_col_transaction_start(col);
2372+
rc = db_col_transaction_start(col, false);
23732373
if (rc != 0)
23742374
goto add_return;
23752375

@@ -2396,9 +2396,9 @@ int db_col_rule_add(struct db_filter_col *col,
23962396

23972397
/* commit the transaction or abort */
23982398
if (rc == 0)
2399-
db_col_transaction_commit(col);
2399+
db_col_transaction_commit(col, false);
24002400
else
2401-
db_col_transaction_abort(col);
2401+
db_col_transaction_abort(col, false);
24022402

24032403
add_return:
24042404
/* update the misc state */
@@ -2415,12 +2415,13 @@ int db_col_rule_add(struct db_filter_col *col,
24152415
/**
24162416
* Start a new seccomp filter transaction
24172417
* @param col the filter collection
2418+
* @param user true if initiated by a user
24182419
*
24192420
* This function starts a new seccomp filter transaction for the given filter
24202421
* collection. Returns zero on success, negative values on failure.
24212422
*
24222423
*/
2423-
int db_col_transaction_start(struct db_filter_col *col)
2424+
int db_col_transaction_start(struct db_filter_col *col, bool user)
24242425
{
24252426
int rc;
24262427
unsigned int iter;
@@ -2439,6 +2440,7 @@ int db_col_transaction_start(struct db_filter_col *col)
24392440
* transaction is current/correct */
24402441

24412442
col->snapshots->shadow = false;
2443+
col->snapshots->user = user;
24422444
return 0;
24432445
}
24442446

@@ -2486,6 +2488,8 @@ int db_col_transaction_start(struct db_filter_col *col)
24862488
} while (rule_o != filter_o->rules);
24872489
}
24882490

2491+
snap->user = user;
2492+
24892493
/* add the snapshot to the list */
24902494
snap->next = col->snapshots;
24912495
col->snapshots = snap;
@@ -2502,11 +2506,12 @@ int db_col_transaction_start(struct db_filter_col *col)
25022506
/**
25032507
* Abort the top most seccomp filter transaction
25042508
* @param col the filter collection
2509+
* @param user true if initiated by a user
25052510
*
25062511
* This function aborts the most recent seccomp filter transaction.
25072512
*
25082513
*/
2509-
void db_col_transaction_abort(struct db_filter_col *col)
2514+
void db_col_transaction_abort(struct db_filter_col *col, bool user)
25102515
{
25112516
int iter;
25122517
unsigned int filter_cnt;
@@ -2524,6 +2529,10 @@ void db_col_transaction_abort(struct db_filter_col *col)
25242529
snap = snap->next;
25252530
_db_snap_release(tmp);
25262531
}
2532+
2533+
if (snap->user != user)
2534+
return;
2535+
25272536
col->snapshots = snap->next;
25282537

25292538
filter_cnt = col->filter_cnt;
@@ -2544,13 +2553,14 @@ void db_col_transaction_abort(struct db_filter_col *col)
25442553
/**
25452554
* Commit the top most seccomp filter transaction
25462555
* @param col the filter collection
2556+
* @param user true if initiated by a user
25472557
*
25482558
* This function commits the most recent seccomp filter transaction and
25492559
* attempts to create a shadow transaction that is a duplicate of the current
25502560
* filter to speed up future transactions.
25512561
*
25522562
*/
2553-
void db_col_transaction_commit(struct db_filter_col *col)
2563+
void db_col_transaction_commit(struct db_filter_col *col, bool user)
25542564
{
25552565
int rc;
25562566
unsigned int iter;
@@ -2573,6 +2583,9 @@ void db_col_transaction_commit(struct db_filter_col *col)
25732583
return;
25742584
}
25752585

2586+
if (snap->user != user)
2587+
return;
2588+
25762589
/* adjust the number of filters if needed */
25772590
if (col->filter_cnt > snap->filter_cnt) {
25782591
unsigned int tmp_i;

src/db.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ struct db_filter_snap {
145145
struct db_filter **filters;
146146
unsigned int filter_cnt;
147147
bool shadow;
148+
bool user;
148149

149150
struct db_filter_snap *next;
150151
};
@@ -215,9 +216,9 @@ int db_col_rule_add(struct db_filter_col *col,
215216
int db_col_syscall_priority(struct db_filter_col *col,
216217
int syscall, uint8_t priority);
217218

218-
int db_col_transaction_start(struct db_filter_col *col);
219-
void db_col_transaction_abort(struct db_filter_col *col);
220-
void db_col_transaction_commit(struct db_filter_col *col);
219+
int db_col_transaction_start(struct db_filter_col *col, bool user);
220+
void db_col_transaction_abort(struct db_filter_col *col, bool user);
221+
void db_col_transaction_commit(struct db_filter_col *col, bool user);
221222

222223
int db_col_precompute(struct db_filter_col *col);
223224
void db_col_precompute_reset(struct db_filter_col *col);

src/python/libseccomp.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ cdef extern from "seccomp.h":
173173
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
174174
size_t *len)
175175

176+
int seccomp_transaction_start(const scmp_filter_ctx ctx)
177+
void seccomp_transaction_reject(const scmp_filter_ctx ctx)
178+
int seccomp_transaction_commit(const scmp_filter_ctx ctx)
179+
176180
int seccomp_precompute(const scmp_filter_ctx ctx)
177181

178182
# kate: syntax python;

src/python/seccomp.pyx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,34 @@ cdef class SyscallFilter:
10791079
raise RuntimeError(str.format("Library error (errno = {0})", rc))
10801080
return program
10811081

1082+
def start_transaction(self):
1083+
""" Start a transaction.
1084+
1085+
Description:
1086+
Start a transaction for modifying the seccomp filter.
1087+
"""
1088+
rc = libseccomp.seccomp_transaction_start(self._ctx)
1089+
if rc != 0:
1090+
raise RuntimeError(str.format("Library error (errno = {0})", rc))
1091+
1092+
def reject_transaction(self):
1093+
""" Reject a transaction.
1094+
1095+
Description:
1096+
Reject the current seccomp filter transaction.
1097+
"""
1098+
libseccomp.seccomp_transaction_reject(self._ctx)
1099+
1100+
def commit_transaction(self):
1101+
""" Commit a transaction.
1102+
1103+
Description:
1104+
Commit the current seccomp filter transaction.
1105+
"""
1106+
rc = libseccomp.seccomp_transaction_commit(self._ctx)
1107+
if rc != 0:
1108+
raise RuntimeError(str.format("Library error (errno = {0})", rc))
1109+
10821110
def precompute(self):
10831111
""" Precompute the seccomp filter.
10841112

tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@ util.pyc
6868
58-live-tsync_notify
6969
59-basic-empty_binary_tree
7070
60-sim-precompute
71+
61-sim-transactions

0 commit comments

Comments
 (0)