import: drain trailing archive null padding in tar-x child
libarchive stops reading as soon as it hits the end-of-archive marker,
but tar archives are usually padded with zeroes to a full record size
after that (GNU tar uses 10K records by default). Since the tar-x child
then exits right away, the importer's final write of that padding can
race against the child exiting and fail with EPIPE:
Imported 88%.
Failed to decode and write: Broken pipe
The child exits successfully in this case, hence no tar error is logged
at all. Seen in TEST-13-NSPAWN on "machinectl import-tar" of a
"tar -czf" generated tarball.
Fix this by reading the input pipe to EOF after tar_x() returns
successfully, verifying that whatever follows the end-of-archive marker
is all zeroes, and refusing the archive otherwise. This affects
importctl import-tar as well as pull-tar and pull-oci.
This addresses Ci failures such as:
https://github.com/systemd/systemd/actions/runs/38738219126/job/116432869392?pr=44163
(cherry picked from commit 9c852031ec8178256196380dc9866553ce37842f)
This commit is contained in:
committed by
Luca Boccassi
parent
4cf713b8ab
commit
385eaf8f94
@@ -16,6 +16,7 @@
|
||||
#include "import-common.h"
|
||||
#include "libarchive-util.h"
|
||||
#include "log.h"
|
||||
#include "memory-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "os-util.h"
|
||||
@@ -27,6 +28,44 @@
|
||||
#include "tar-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
static int drain_null_padding(int fd) {
|
||||
uint64_t n = 0;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* libarchive stops reading as soon as it encounters the end-of-archive marker, but tar archives are
|
||||
* usually padded with zeroes to a full record size (10K for GNU tar) after it. Read and discard
|
||||
* that, so that the writing side doesn't get EPIPE when it pushes the final bytes after we exited
|
||||
* already. Insist that this padding is actually all zeroes though, and refuse any other trailing
|
||||
* garbage. */
|
||||
|
||||
for (;;) {
|
||||
uint8_t buf[64U*1024U];
|
||||
ssize_t l;
|
||||
|
||||
l = read(fd, buf, sizeof(buf));
|
||||
if (l < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
return log_error_errno(errno, "Failed to read trailing archive padding: %m");
|
||||
}
|
||||
if (l == 0)
|
||||
break;
|
||||
|
||||
if (!memeqzero(buf, l))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Non-zero trailing data after end of archive, refusing.");
|
||||
|
||||
n += l;
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
log_debug("Discarded %" PRIu64 " bytes of zero padding after end of archive.", n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
||||
int r;
|
||||
|
||||
@@ -97,6 +136,9 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
||||
if (tar_x(pipefd[0], tree_fd, flags) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
if (drain_null_padding(pipefd[0]) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user