Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFE: add transaction support to the libseccomp API #415

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

pcmoore
Copy link
Member

@pcmoore pcmoore commented Sep 21, 2023

This PR contains two patches, the first fixes an existing problem with transaction management and the second adds a new transaction API to libseccomp.

@drakenclimber when you have the chance please take a look and let me know what you think, especially regarding the API as that is difficult/impossible to change later. If everything looks okay, I'll submit another PR with the "src/db.c" fix for the release-2.5 branch,

Copy link
Member

@drakenclimber drakenclimber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patchset, @pcmoore. I think the user-facing API is good. I do have a few questions here and there but nothing earth shattering.

@@ -2559,8 +2566,9 @@ void db_col_transaction_commit(struct db_filter_col *col)
if (snap->shadow) {
/* leave the shadow intact, but drop the next snapshot */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this block of code accessible? I hacked around for just a bit and was unable to hit it.

snap->shadow is effectively always set to false at the start of db_col_transaction_start(). snap->shadow is only set true at the end of db_col_transaction_commit() - approximately 100 lines after this. Is there a code path where snap->shadow is true at this point?

_db_snap_release(snap->next);
struct db_filter_snap *tmp = snap->next;
snap->next = tmp->next;
_db_snap_release(tmp);
}
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This comment is for a couple lines down, but Github won't let me comment there. Specifically this line: https://github.com/pcmoore/misc-libseccomp/blob/working-transactions/src/db.c#L2589)

Since we had issues handling aborts, I wrote a quick test for this large block of code where the col's filter count didn't match the snap's filter count.

Here's the test I threw together:
drakenclimber@3814774

And here's the code coverage (prior to this test these lines weren't being hit):
https://coveralls.io/builds/62918086/source?filename=src%2Fdb.c#L2589

Interestingly this leads to a valgrind failure, but I haven't looked into it yet.

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this code block as it clearly explains why a user would be interested in this feature

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you have outlined in issue #181, this should greatly help in testing transactions 👍. Do you have a use case in mind for libseccomp's users (like systemd, etc.)?

* This function rejects the current seccomp filter transaction.
*
*/
void seccomp_transaction_reject(const scmp_filter_ctx ctx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we ever want a return value here? I can't think of one, but as you mentioned in the cover letter, the API is set in stone once we release this :)

@@ -145,6 +145,7 @@ struct db_filter_snap {
struct db_filter **filters;
unsigned int filter_cnt;
bool shadow;
bool user;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking a bool should meet our needs initially, but I'm wondering if long-term we'll want to switch to a transaction number (with a special bit for user-initiated transactions). I'm afraid the bool check of if snap->user != user may not be powerful enough someday.

The good thing is none of this exposed by the API, so we could change it later.

@drakenclimber
Copy link
Member

Valgrind output of the test I added:

==1041378== Memcheck, a memory error detector
==1041378== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==1041378== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==1041378== Command: ./62-sim-arch_transactions -b
==1041378==
--1041378-- WARNING: unhandled amd64-linux syscall: 317
--1041378-- You may be able to write your own handler.
--1041378-- Read the file README_MISSING_SYSCALL_OR_IOCTL.
--1041378-- Nevertheless we consider this a bug.  Please report
--1041378-- it at http:https://valgrind.org/support/bug_reports.html.
==1041378== Invalid read of size 8
==1041378==    at 0x406A09: db_col_transaction_commit (db.c:2616)
==1041378==    by 0x402930: seccomp_transaction_commit (api.c:831)
==1041378==    by 0x401375: main (62-sim-arch_transactions.c:92)
==1041378==  Address 0x4a4e9c0 is 0 bytes after a block of size 16 alloc'd
==1041378==    at 0x484782C: calloc (vg_replace_malloc.c:1554)
==1041378==    by 0x4064EB: db_col_transaction_start (db.c:2451)
==1041378==    by 0x4028D9: seccomp_transaction_start (api.c:806)
==1041378==    by 0x401345: main (62-sim-arch_transactions.c:85)
==1041378==
==1041378==
==1041378== HEAP SUMMARY:
==1041378==     in use at exit: 312 bytes in 3 blocks
==1041378==   total heap usage: 102 allocs, 99 frees, 9,864 bytes allocated
==1041378==
==1041378== 312 (32 direct, 280 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==1041378==    at 0x484782C: calloc (vg_replace_malloc.c:1554)
==1041378==    by 0x406543: _db_init (db.c:873)
==1041378==    by 0x406543: db_col_transaction_start (db.c:2465)
==1041378==    by 0x4028D9: seccomp_transaction_start (api.c:806)
==1041378==    by 0x401345: main (62-sim-arch_transactions.c:85)
==1041378==
==1041378== LEAK SUMMARY:
==1041378==    definitely lost: 32 bytes in 1 blocks
==1041378==    indirectly lost: 280 bytes in 2 blocks
==1041378==      possibly lost: 0 bytes in 0 blocks
==1041378==    still reachable: 0 bytes in 0 blocks
==1041378==         suppressed: 0 bytes in 0 blocks
==1041378==
==1041378== For lists of detected and suppressed errors, rerun with: -s
==1041378== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

@drakenclimber
Copy link
Member

drakenclimber commented Oct 18, 2023

Well unsurprisingly these are the lines that valgrind is angry about:

		/* NOTE: while we release the filters we no longer need, we
		 *       don't bother to resize the filter array, we just
		 *       adjust the filter counter, this *should* be harmless
		 *       at the cost of a not reaping all the memory possible */

		do {
			_db_release(snap->filters[snap->filter_cnt--]);
		} while (snap->filter_cnt > col->filter_cnt);

@drakenclimber
Copy link
Member

drakenclimber commented Oct 18, 2023

As expected, resizing the snap filter array appeases valgrind. I feel like it's a worthwhile change since this is likely an infrequent operation (and perhaps only in an error path). I think proper memory management may outweigh speed in this case.

Also, I don't claim to like my implementation - it was just a proof of concept to verify the root cause :).

@@ -2612,8 +2613,17 @@ void db_col_transaction_commit(struct db_filter_col *col, bool user)
                 *       adjust the filter counter, this *should* be harmless
                 *       at the cost of a not reaping all the memory possible */
                do {
+                       struct db_filter **dbs;
+
                        _db_release(snap->filters[snap->filter_cnt--]);
+
+                       dbs = realloc(snap->filters,
+                                     sizeof(struct db_filter *) * snap->filter_cnt);
+                       if (dbs != NULL)
+                               snap->filters = dbs;
                } while (snap->filter_cnt > col->filter_cnt);
        }

It turns out we don't properly handle transaction aborts as we should,
this likely broke when we implemented the shadow snapshots but as the
transaction concept was not exported via the API, and callers most
likely abandoned the filter on error this went unnoticed.

This patch ensures that transaction aborts are handled properly by
correctly managing the filter's transaction stack.

Signed-off-by: Paul Moore <[email protected]>
@coveralls
Copy link

coveralls commented Apr 8, 2024

Coverage Status

coverage: 89.708% (+0.2%) from 89.474%
when pulling 0deb5a2 on pcmoore:working-transactions
into 47ca644 on seccomp:main.

Fix an off-by-one error that was causing us to leak a db_filter struct
at transaction commit time when we removed an arch/ABI in a transaction.

Reported-by: Tom Hromatka <[email protected]>
Signed-off-by: Paul Moore <[email protected]>
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]>
@drakenclimber
Copy link
Member

I have a rough schedule the next few days, but I should have time at the end of the week to check this (and the other libseccomp patches) out later in the week.

@pcmoore
Copy link
Member Author

pcmoore commented Apr 14, 2024

I have a rough schedule the next few days, but I should have time at the end of the week to check this (and the other libseccomp patches) out later in the week.

Hi Tom, no immediate rush, but when you have the time it would be great to look over the other PRs; if you don't have time to merge them - assuming they look good to you - just leave your ACK and I'll take care of them.

As far as this PR is concerned, don't spend your time reviewing it just yet, I want to finish up a few things and make sure everything looks good on my end first; I'll leave a note when it is ready for review. Thanks for the previous review and feedback, it's been very helpful.

@pcmoore pcmoore marked this pull request as draft April 14, 2024 14:37
@drakenclimber
Copy link
Member

I have a rough schedule the next few days, but I should have time at the end of the week to check this (and the other libseccomp patches) out later in the week.

Hi Tom, no immediate rush, but when you have the time it would be great to look over the other PRs; if you don't have time to merge them - assuming they look good to you - just leave your ACK and I'll take care of them.

As far as this PR is concerned, don't spend your time reviewing it just yet, I want to finish up a few things and make sure everything looks good on my end first; I'll leave a note when it is ready for review. Thanks for the previous review and feedback, it's been very helpful.

Sounds good. I'll focus on the other PRs this week. Let me know when this one is ready, and I'll start looking at it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RFE: expose the transaction mechanism for use by applications
3 participants