-
Notifications
You must be signed in to change notification settings - Fork 300
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
core: Always sort incoming xattrs #3346
base: main
Are you sure you want to change the base?
Conversation
tests/test-basic-c.c
Outdated
g_assert_no_error (error); | ||
} | ||
|
||
/* And now with a swapped order */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I also verified that this test successfully fails if we drop the xattr canonicalization.
I get the following error when pulling an image into ostree layered on top of quay.io/fedora/fedora-kinoite. It has user.* xattrs in the container tar stream.
EDIT: If you want to see the CI job, it is here: https://github.com/prydom/my-ostree-build/actions/runs/12056266866/job/33618838686. The build container has ostree and ostree-libs build from commit 2be4c52. |
When recomputing selinux attrs during commit, we weren't sorting, which could cause various issues like fsck failures. This is a big hammer; change things so we always canonicalize (i.e. sort) the incoming xattrs when creating a file header and directory metadata. I think almost all places in the code were already keeping things sorted, but it's better to ensure correctness first. If we ever have some performance issue (I'm doubtful) we could add something like `_ostree_file_header_known_canonicalized` or so. Closes: ostreedev#3343 Signed-off-by: Colin Walters <[email protected]>
2be4c52
to
c863558
Compare
Thanks for testing this out! I pushed an update that also covers directory metadata - only unit tested here so far. |
I've done another round of end-to-end testing and the changes work well for me. On github actions:
In
On target machine:
|
GVariant *b = *(GVariant **)b_pp; | ||
const char *name_a; | ||
const char *name_b; | ||
g_variant_get (a, "(^&ay@ay)", &name_a, NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random: you could just memcmp()
the data of the GVariants directly: the key starts at byte 0. That's pretty evil, though.
Otherwise it might be a bit nicer to use g_variant_get_child(a, 0, "&ay", &name_a)
|
||
// Sort xattrs by name | ||
static GVariant * | ||
canonicalize_xattrs (GVariant *xattrs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes. This function is complicated, but I honestly can't think of any other way to do it more simply...
One thing I'd consider is scanning the "input" to see if it's already in order (trivially true also for the common case of only one item) and return a ref directly in that case.
for (guint i = 0; i < n; i++) | ||
{ | ||
const guint8 *name; | ||
const char *name; | ||
g_autoptr (GVariant) value = NULL; | ||
g_variant_get_child (xattrs, i, "(^&ay@ay)", &name, &value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulling the (unused) value
out here just to have it auto-freed at the end of the loop is wasteful. You could just give NULL
there. This whole loop could also be converted to a GVariantIter
.
= "72473a9e8ace75f89f1504137cfb134feb5372bc1d97e04eb5300e24ad836153"; | ||
|
||
GVariantBuilder builder; | ||
g_autoptr (GVariant) inorder_xattrs = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could move the declaration down to the same line as the assignment, like you do for unsorted...
g_autoptr (GVariant) unsorted_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder)); | ||
|
||
/* Now test with xattrs */ | ||
ostree_repo_write_regfile_inline (repo, expected_sha256_with_xattrs, 0, 0, S_IFREG | 0644, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you also want to check the return value?
ostree_repo_write_regfile_inline (repo, expected_sha256_with_xattrs, 0, 0, S_IFREG | 0644, | ||
unsorted_xattrs, (guint8 *)"hi", 2, NULL, &error); | ||
g_assert_no_error (error); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice that you don't have a testcase for what happens if someone tries to repeat an xattr. If I understand the code above, you'd sort the incoming list, but fail to detect the duplicate, which would later fail to validate.
Do you want to add that check as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also don't check for incoming empty xattr key values. That might be another nice thing to figure out, and then add a test for.
When recomputing selinux attrs during commit, we weren't sorting, which could cause various issues like fsck failures.
This is a big hammer; change things so we always canonicalize (i.e. sort) the incoming xattrs when creating a file header.
I think almost all places in the code were already keeping things sorted, but it's better to ensure correctness first. If we ever have some performance issue (I'm doubtful) we could add something like
_ostree_file_header_known_canonicalized
or so.Closes: #3343