Skip to content

Commit

Permalink
api: add transaction support to the libseccomp API
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
pcmoore committed Sep 21, 2023
1 parent 6255796 commit 6adf397
Show file tree
Hide file tree
Showing 12 changed files with 460 additions and 12 deletions.
134 changes: 134 additions & 0 deletions doc/man/man3/seccomp_transaction_start.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
.TH "seccomp_transaction_start" 3 "21 September 2023" "[email protected]" "libseccomp Documentation"
.\" //////////////////////////////////////////////////////////////////////////
.SH NAME
.\" //////////////////////////////////////////////////////////////////////////
seccomp_transaction_start, seccomp_transaction_commit, seccomp_transaction_reject \- Manage seccomp filter transactions
.\" //////////////////////////////////////////////////////////////////////////
.SH SYNOPSIS
.\" //////////////////////////////////////////////////////////////////////////
.nf
.B #include <seccomp.h>
.sp
.B typedef void * scmp_filter_ctx;
.sp
.BI "int seccomp_transaction_start(scmp_filter_ctx " ctx ");
.BI "int seccomp_transaction_commit(scmp_filter_ctx " ctx ");
.BI "void seccomp_transaction_reject(scmp_filter_ctx " ctx ");
.sp
Link with \fI\-lseccomp\fP.
.fi
.\" //////////////////////////////////////////////////////////////////////////
.SH DESCRIPTION
.\" //////////////////////////////////////////////////////////////////////////
.P
The
.BR seccomp_transaction_start ()
function starts a new seccomp filter
transaction that the caller can use to perform any number of filter
modifications which can then be committed to the filter using
.BR seccomp_transaction_commit ()
or rejected using
.BR seccomp_transaction_reject ().
It is important to note that transactions only affect the seccomp filter state
while it is being managed by libseccomp; seccomp filters which have been loaded
into the kernel can not be modified, only new seccomp filters can be added on
top of the existing loaded filter stack.
.P
Finishing, or committing, a transaction is optional, although it is encouraged.
At any point in time, regardless of the transaction state, the seccomp filter
is determined by all of the libseccomp operations performed on the filter up to
that point. Committing a transaction simply flushes the transaction rollback
marker of the current transaction making the filter changes permanent;
rejecting a transaction rolls the filter state back to immediately before the
transaction was started.
.P
Transactions can be nested arbitrarily deep with the
.BR seccomp_transaction_commit ()
and
.BR seccomp_transaction_reject ()
functions always operating on the deepest, or more recently started transaction.
A nested set of filter modifications, even if committed, is still subject to
rejection by shallower, or older transactions that have yet to be committed or
rejected.
.\" //////////////////////////////////////////////////////////////////////////
.SH RETURN VALUE
.\" //////////////////////////////////////////////////////////////////////////
The
.BR seccomp_transaction_start ()
and
.BR seccomp_transaction_commit ()
functions return zero on success or one of the following error codes on
failure:
.TP
.B -ENOMEM
The library was unable to allocate enough memory.
.\" //////////////////////////////////////////////////////////////////////////
.SH EXAMPLES
.\" //////////////////////////////////////////////////////////////////////////
.nf
#include <seccomp.h>

int libseccomp_generate(scmp_filter_ctx *ctx)
{
int rc;

rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
if (rc)
return rc;
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
if (rc)
return rc;
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
if (rc)
return rc;

return 0;
}

int main(int argc, char *argv[])
{
int rc = \-1;
scmp_filter_ctx ctx;

ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL)
goto out;

rc = seccomp_transaction_start(ctx)
if (rc)
goto out;
rc = libseccomp_generate(ctx);
if (rc == 0) {
rc = seccomp_transaction_commit(ctx);
if (rc)
goto out;
} else
seccomp_transaction_reject(ctx);

/* ... */

out:
seccomp_release(ctx);
return \-rc;
}
.fi
.\" //////////////////////////////////////////////////////////////////////////
.SH NOTES
.\" //////////////////////////////////////////////////////////////////////////
.P
While the seccomp filter can be generated independent of the kernel, kernel
support is required to load and enforce the seccomp filter generated by
libseccomp.
.P
The libseccomp project site, with more information and the source code
repository, can be found at https://github.com/seccomp/libseccomp. This tool,
as well as the libseccomp library, is currently under development, please
report any bugs at the project site or directly to the author.
.\" //////////////////////////////////////////////////////////////////////////
.SH AUTHOR
.\" //////////////////////////////////////////////////////////////////////////
Paul Moore <[email protected]>
.\" //////////////////////////////////////////////////////////////////////////
.SH SEE ALSO
.\" //////////////////////////////////////////////////////////////////////////
.BR seccomp_init (3),
29 changes: 29 additions & 0 deletions include/seccomp.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,35 @@ int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd);
*/
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf, size_t *len);

/**
* Start a filter transaction
* @param ctx the filter context
*
* This function starts a filter transaction for modifying the seccomp filter.
* Returns zero on success, negative values on failure.
*
*/
int seccomp_transaction_start(const scmp_filter_ctx ctx);

/**
* Reject the current filter transaction
* @param ctx the filter context
*
* This function rejects the current seccomp filter transaction.
*
*/
void seccomp_transaction_reject(const scmp_filter_ctx ctx);

/**
* Commit the current filter transaction
* @param ctx the filter context
*
* This function commits the current seccomp filter transaction. Returns zero
* on success, negative values on failure.
*
*/
int seccomp_transaction_commit(const scmp_filter_ctx ctx);

/**
* Precompute the seccomp filter for future use
* @param ctx the filter context
Expand Down
39 changes: 39 additions & 0 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,45 @@ API int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
return rc;
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_transaction_start(const scmp_filter_ctx ctx)
{
int rc;
struct db_filter_col *col;

if (_ctx_valid(ctx))
return _rc_filter(-EINVAL);
col = (struct db_filter_col *)ctx;

rc = db_col_transaction_start(col, true);
return _rc_filter(rc);
}

/* NOTE - function header comment in include/seccomp.h */
API void seccomp_transaction_reject(const scmp_filter_ctx ctx)
{
struct db_filter_col *col;

if (_ctx_valid(ctx))
return;
col = (struct db_filter_col *)ctx;

db_col_transaction_abort(col, true);
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_transaction_commit(const scmp_filter_ctx ctx)
{
struct db_filter_col *col;

if (_ctx_valid(ctx))
return _rc_filter(-EINVAL);
col = (struct db_filter_col *)ctx;

db_col_transaction_commit(col, true);
return _rc_filter(0);
}

/* NOTE - function header comment in include/seccomp.h */
API int seccomp_precompute(const scmp_filter_ctx ctx)
{
Expand Down
24 changes: 18 additions & 6 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -2369,7 +2369,7 @@ int db_col_rule_add(struct db_filter_col *col,
}

/* create a checkpoint */
rc = db_col_transaction_start(col);
rc = db_col_transaction_start(col, false);
if (rc != 0)
goto add_return;

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

/* commit the transaction or abort */
if (rc == 0)
db_col_transaction_commit(col);
db_col_transaction_commit(col, false);
else
db_col_transaction_abort(col);
db_col_transaction_abort(col, false);

add_return:
/* update the misc state */
Expand All @@ -2415,12 +2415,13 @@ int db_col_rule_add(struct db_filter_col *col,
/**
* Start a new seccomp filter transaction
* @param col the filter collection
* @param user true if initiated by a user
*
* This function starts a new seccomp filter transaction for the given filter
* collection. Returns zero on success, negative values on failure.
*
*/
int db_col_transaction_start(struct db_filter_col *col)
int db_col_transaction_start(struct db_filter_col *col, bool user)
{
int rc;
unsigned int iter;
Expand All @@ -2439,6 +2440,7 @@ int db_col_transaction_start(struct db_filter_col *col)
* transaction is current/correct */

col->snapshots->shadow = false;
col->snapshots->user = user;
return 0;
}

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

snap->user = user;

/* add the snapshot to the list */
snap->next = col->snapshots;
col->snapshots = snap;
Expand All @@ -2502,11 +2506,12 @@ int db_col_transaction_start(struct db_filter_col *col)
/**
* Abort the top most seccomp filter transaction
* @param col the filter collection
* @param user true if initiated by a user
*
* This function aborts the most recent seccomp filter transaction.
*
*/
void db_col_transaction_abort(struct db_filter_col *col)
void db_col_transaction_abort(struct db_filter_col *col, bool user)
{
int iter;
unsigned int filter_cnt;
Expand All @@ -2524,6 +2529,10 @@ void db_col_transaction_abort(struct db_filter_col *col)
snap = snap->next;
_db_snap_release(tmp);
}

if (snap->user != user)
return;

col->snapshots = snap->next;

filter_cnt = col->filter_cnt;
Expand All @@ -2544,13 +2553,14 @@ void db_col_transaction_abort(struct db_filter_col *col)
/**
* Commit the top most seccomp filter transaction
* @param col the filter collection
* @param user true if initiated by a user
*
* This function commits the most recent seccomp filter transaction and
* attempts to create a shadow transaction that is a duplicate of the current
* filter to speed up future transactions.
*
*/
void db_col_transaction_commit(struct db_filter_col *col)
void db_col_transaction_commit(struct db_filter_col *col, bool user)
{
int rc;
unsigned int iter;
Expand All @@ -2561,6 +2571,8 @@ void db_col_transaction_commit(struct db_filter_col *col)
snap = col->snapshots;
if (snap == NULL)
return;
if (snap->user != user)
return;

/* check for a shadow set by a higher transaction commit */
if (snap->shadow) {
Expand Down
7 changes: 4 additions & 3 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ struct db_filter_snap {
struct db_filter **filters;
unsigned int filter_cnt;
bool shadow;
bool user;

struct db_filter_snap *next;
};
Expand Down Expand Up @@ -215,9 +216,9 @@ int db_col_rule_add(struct db_filter_col *col,
int db_col_syscall_priority(struct db_filter_col *col,
int syscall, uint8_t priority);

int db_col_transaction_start(struct db_filter_col *col);
void db_col_transaction_abort(struct db_filter_col *col);
void db_col_transaction_commit(struct db_filter_col *col);
int db_col_transaction_start(struct db_filter_col *col, bool user);
void db_col_transaction_abort(struct db_filter_col *col, bool user);
void db_col_transaction_commit(struct db_filter_col *col, bool user);

int db_col_precompute(struct db_filter_col *col);
void db_col_precompute_reset(struct db_filter_col *col);
Expand Down
4 changes: 4 additions & 0 deletions src/python/libseccomp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ cdef extern from "seccomp.h":
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
size_t *len)

int seccomp_transaction_start(const scmp_filter_ctx ctx)
void seccomp_transaction_reject(const scmp_filter_ctx ctx)
int seccomp_transaction_commit(const scmp_filter_ctx ctx)

int seccomp_precompute(const scmp_filter_ctx ctx)

# kate: syntax python;
Expand Down
28 changes: 28 additions & 0 deletions src/python/seccomp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,34 @@ cdef class SyscallFilter:
raise RuntimeError(str.format("Library error (errno = {0})", rc))
return program

def start_transaction(self):
""" Start a transaction.
Description:
Start a transaction for modifying the seccomp filter.
"""
rc = libseccomp.seccomp_transaction_start(self._ctx)
if rc != 0:
raise RuntimeError(str.format("Library error (errno = {0})", rc))

def reject_transaction(self):
""" Reject a transaction.
Description:
Reject the current seccomp filter transaction.
"""
libseccomp.seccomp_transaction_reject(self._ctx)

def commit_transaction(self):
""" Commit a transaction.
Description:
Commit the current seccomp filter transaction.
"""
rc = libseccomp.seccomp_transaction_commit(self._ctx)
if rc != 0:
raise RuntimeError(str.format("Library error (errno = {0})", rc))

def precompute(self):
""" Precompute the seccomp filter.
Expand Down
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ util.pyc
58-live-tsync_notify
59-basic-empty_binary_tree
60-sim-precompute
61-sim-transactions
Loading

0 comments on commit 6adf397

Please sign in to comment.