Skip to content

Commit

Permalink
unpack: make sure stdout is locked
Browse files Browse the repository at this point in the history
When reading malformed archives, unpack may write error messages to
stdout from multiple threads. This can result in interleaved output
and garbled messages. To prevent this, a static mutex is added to
output functions in common.c.
  • Loading branch information
Gottox committed Dec 12, 2024
1 parent d1e8116 commit 00bef59
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 25 deletions.
13 changes: 13 additions & 0 deletions tools/include/sqshtools_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,17 @@ void print_raw(const char *segment, size_t segment_size);

void print_escaped(const char *segment, size_t segment_size);

void locked_perror(const char *msg);

void locked_sqsh_perror(int error_code, const char *msg);

void locked_perror(const char *msg);

__attribute__((__format__(__printf__, 2, 0))) void
locked_fprintf(FILE *stream, const char *format, ...);

void locked_fputs(const char *s, FILE *stream);

int locked_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

#endif /* TOOLS_COMMON_H */
61 changes: 53 additions & 8 deletions tools/src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,52 @@
* @created Wednesday Jun 07, 2023 15:41:30 CEST
*/

#include "sqsh_error.h"
#include <pthread.h>
#include <sqshtools_common.h>
#include <stdarg.h>

pthread_mutex_t output_lock = PTHREAD_MUTEX_INITIALIZER;

void
locked_perror(const char *msg) {
pthread_mutex_lock(&output_lock);
perror(msg);
pthread_mutex_unlock(&output_lock);
}

Check warning on line 47 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L43-L47

Added lines #L43 - L47 were not covered by tests

void
locked_fprintf(FILE *stream, const char *format, ...) {

Check warning on line 50 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L50

Added line #L50 was not covered by tests
va_list args;
va_start(args, format);
pthread_mutex_lock(&output_lock);
vfprintf(stream, format, args);
pthread_mutex_unlock(&output_lock);
va_end(args);
}

Check warning on line 57 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L52-L57

Added lines #L52 - L57 were not covered by tests

void
locked_fputs(const char *s, FILE *stream) {
pthread_mutex_lock(&output_lock);
fputs(s, stream);
pthread_mutex_unlock(&output_lock);
}

int
locked_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
int ret;
pthread_mutex_lock(&output_lock);
ret = fwrite(ptr, size, nmemb, stream);
pthread_mutex_unlock(&output_lock);
return ret;
}

void
locked_sqsh_perror(int error_code, const char *msg) {
pthread_mutex_lock(&output_lock);
sqsh_perror(error_code, msg);
pthread_mutex_unlock(&output_lock);
}

struct SqshArchive *
open_archive(const char *image_path, uint64_t offset, int *err) {
Expand All @@ -55,27 +100,27 @@ open_archive(const char *image_path, uint64_t offset, int *err) {

void
print_raw(const char *segment, size_t segment_size) {
fwrite(segment, 1, segment_size, stdout);
locked_fwrite(segment, 1, segment_size, stdout);
}

void
print_escaped(const char *segment, size_t segment_size) {
for (size_t i = 0; i < segment_size; i++) {
switch (segment[i]) {
case '\n':
fputs("\\n", stdout);
locked_fputs("\\n", stdout);

Check warning on line 111 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L111

Added line #L111 was not covered by tests
break;
case '\r':
fputs("\\r", stdout);
locked_fputs("\\r", stdout);

Check warning on line 114 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L114

Added line #L114 was not covered by tests
break;
case '\t':
fputs("\\t", stdout);
locked_fputs("\\t", stdout);

Check warning on line 117 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L117

Added line #L117 was not covered by tests
break;
case '\\':
fputs("\\\\", stdout);
locked_fputs("\\\\", stdout);

Check warning on line 120 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L120

Added line #L120 was not covered by tests
break;
case '\x1b':
fputs("\\e", stdout);
locked_fputs("\\e", stdout);
break;
case 0x01:
case 0x02:
Expand Down Expand Up @@ -106,10 +151,10 @@ print_escaped(const char *segment, size_t segment_size) {
case 0x1f:
case 0x20:
case 0x7f:
printf("\\x%02x", segment[i]);
locked_fprintf(stdout, "\\x%02x", segment[i]);

Check warning on line 154 in tools/src/common.c

View check run for this annotation

Codecov / codecov/patch

tools/src/common.c#L154

Added line #L154 was not covered by tests
break;
default:
putchar(segment[i]);
locked_fputs((char[2]){segment[i], 0}, stdout);
break;
}
}
Expand Down
33 changes: 16 additions & 17 deletions tools/src/unpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
Expand Down Expand Up @@ -88,14 +87,14 @@ update_metadata(const char *path, const struct SqshFile *file) {
times[0].tv_sec = times[1].tv_sec = sqsh_file_modified_time(file);
rv = utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW);
if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 90 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L90

Added line #L90 was not covered by tests
goto out;
}

uint16_t mode = sqsh_file_permission(file);
rv = fchmodat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW);
if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 97 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L97

Added line #L97 was not covered by tests
goto out;
}

Expand All @@ -105,7 +104,7 @@ update_metadata(const char *path, const struct SqshFile *file) {

rv = chown(path, uid, gid);
if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 107 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L107

Added line #L107 was not covered by tests
goto out;
}
}
Expand All @@ -122,17 +121,17 @@ extract_dir(const char *path) {
struct stat st;
rv = stat(path, &st);
if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 124 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L124

Added line #L124 was not covered by tests
goto out;
}
if (!S_ISDIR(st.st_mode)) {
errno = EEXIST;
perror(path);
locked_perror(path);

Check warning on line 129 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L129

Added line #L129 was not covered by tests
goto out;
}

} else if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 134 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L134

Added line #L134 was not covered by tests
goto out;
}

Expand All @@ -147,7 +146,7 @@ update_metadata_dir(const char *path, const struct SqshFile *file) {
int rv = 0;
rv = chmod(path, sqsh_file_permission(file));
if (rv < 0) {
perror(path);
locked_perror(path);

Check warning on line 149 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L149

Added line #L149 was not covered by tests
goto out;
}

Expand Down Expand Up @@ -180,7 +179,7 @@ extract_file_after(
int rv = 0;
struct ExtractFileData *data = d;
if (err < 0) {
sqsh_perror(err, data->path);
locked_sqsh_perror(err, data->path);

Check warning on line 182 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L182

Added line #L182 was not covered by tests
}

rv = rename(data->tmp_filename, data->path);
Expand All @@ -194,7 +193,7 @@ extract_file_after(
out:
cx_semaphore_post(&file_descriptor_sem);
if (rv < 0) {
sqsh_perror(rv, data->path);
locked_sqsh_perror(rv, data->path);

Check warning on line 196 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L196

Added line #L196 was not covered by tests
}
extract_file_cleanup(data, NULL);
}
Expand Down Expand Up @@ -261,7 +260,7 @@ extract_file(const char *path, const struct SqshFile *file) {
}
out:
if (rv < 0) {
sqsh_perror(rv, path);
locked_sqsh_perror(rv, path);

Check warning on line 263 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L263

Added line #L263 was not covered by tests
extract_file_cleanup(data, stream);
if (fd > 0) {
close(fd);
Expand Down Expand Up @@ -389,7 +388,7 @@ extract_from_traversal(
} else {
file = sqsh_tree_traversal_open_file(iter, &rv);
if (rv < 0) {
sqsh_perror(rv, path);
locked_sqsh_perror(rv, path);

Check warning on line 391 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L391

Added line #L391 was not covered by tests
goto out;
}

Expand Down Expand Up @@ -423,14 +422,14 @@ extract_all(

iter = sqsh_tree_traversal_new(base, &rv);
if (rv < 0) {
sqsh_perror(rv, image_path);
locked_sqsh_perror(rv, image_path);

Check warning on line 425 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L425

Added line #L425 was not covered by tests
goto out;
}

bool has_next = sqsh_tree_traversal_next(iter, &rv);
if (!has_next) {
rv = -SQSH_ERROR_INTERNAL;
sqsh_perror(rv, image_path);
locked_sqsh_perror(rv, image_path);

Check warning on line 432 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L432

Added line #L432 was not covered by tests
goto out;
}

Expand All @@ -441,7 +440,7 @@ extract_all(
}
}
if (rv < 0) {
sqsh_perror(rv, image_path);
locked_sqsh_perror(rv, image_path);

Check warning on line 443 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L443

Added line #L443 was not covered by tests
goto out;
}

Expand Down Expand Up @@ -505,7 +504,7 @@ main(int argc, char *argv[]) {

sqsh = open_archive(image_path, offset, &rv);
if (rv < 0) {
sqsh_perror(rv, image_path);
locked_sqsh_perror(rv, image_path);
rv = EXIT_FAILURE;
goto out;
}
Expand All @@ -528,7 +527,7 @@ main(int argc, char *argv[]) {

src_root = sqsh_lopen(sqsh, src_path, &rv);
if (rv < 0) {
sqsh_perror(rv, src_path);
locked_sqsh_perror(rv, src_path);

Check warning on line 530 in tools/src/unpack.c

View check run for this annotation

Codecov / codecov/patch

tools/src/unpack.c#L530

Added line #L530 was not covered by tests
rv = EXIT_FAILURE;
goto out;
}
Expand Down

0 comments on commit 00bef59

Please sign in to comment.