tools

various tools I have been using throughout the years
Log | Files | Refs | README | LICENSE

commit 3f437ecbe8d96a90bb3d3ca81c191be2223b4dda
parent 2cbc7f8ae2cc154b3e0642b34c0bac4abb3c5bb0
Author: mtmn <miro@haravara.org>
Date:   Sat, 23 May 2026 02:23:23 +0200

mpd: apply findings from fuzzing

Diffstat:
Mmpd/BUILD | 33++++++++++++++++++++++++++++-----
Mmpd/mpd_add_to_playlist.c | 21++++++++++++---------
Mmpd/mpd_add_to_queue.c | 37+++++++++++++++++++++++--------------
Mmpd/mpd_edit_queue.c | 74++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mmpd/mpd_update_library.c | 272++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mmpd/mpd_update_queue.c | 56+++++++++++++++++++++++++++++---------------------------
6 files changed, 278 insertions(+), 215 deletions(-)

diff --git a/mpd/BUILD b/mpd/BUILD @@ -9,13 +9,36 @@ COMMON_COPTS = [ "-O2", "-fanalyzer", "-Wshadow", + "-fstack-protector-strong", + "-D_FORTIFY_SOURCE=2", + "-fPIE", + "-Wformat", + "-Werror=format-security", + "-Wnull-dereference", + "-Wdangling-pointer", + "-Wtrampolines", + "-Walloca", + "-Wcast-align=strict", + "-Wdate-time", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wredundant-decls", + "-Wswitch-default", + "-Wvla", + "-Wwrite-strings", +] + +COMMON_LINKOPTS = [ + "-lmpdclient", + "-Wl,-z,relro", + "-Wl,-z,now", ] cc_binary( name = "mpd_add_to_playlist", srcs = ["mpd_add_to_playlist.c"], copts = COMMON_COPTS, - linkopts = ["-lmpdclient"], + linkopts = COMMON_LINKOPTS, visibility = ["//visibility:public"], ) @@ -23,7 +46,7 @@ cc_binary( name = "mpd_add_to_queue", srcs = ["mpd_add_to_queue.c"], copts = COMMON_COPTS, - linkopts = ["-lmpdclient"], + linkopts = COMMON_LINKOPTS, visibility = ["//visibility:public"], ) @@ -31,7 +54,7 @@ cc_binary( name = "mpd_update_queue", srcs = ["mpd_update_queue.c"], copts = COMMON_COPTS, - linkopts = ["-lmpdclient"], + linkopts = COMMON_LINKOPTS, visibility = ["//visibility:public"], ) @@ -39,7 +62,7 @@ cc_binary( name = "mpd_edit_queue", srcs = ["mpd_edit_queue.c"], copts = COMMON_COPTS, - linkopts = ["-lmpdclient"], + linkopts = COMMON_LINKOPTS, visibility = ["//visibility:public"], ) @@ -47,7 +70,7 @@ cc_binary( name = "mpd_update_library", srcs = ["mpd_update_library.c"], copts = COMMON_COPTS, - linkopts = ["-lmpdclient"], + linkopts = COMMON_LINKOPTS, visibility = ["//visibility:public"], ) diff --git a/mpd/mpd_add_to_playlist.c b/mpd/mpd_add_to_playlist.c @@ -34,20 +34,23 @@ struct mpd_connection *conn() { D(printf("%s %s:%u\n", "Connecting to", host, port)); struct mpd_connection *c = mpd_connection_new(host, port, 0); - enum mpd_error err = mpd_connection_get_error(c); - if (err != 0) { - printf("Error code: %u. View error codes here: " - "https://www.musicpd.org/doc/libmpdclient/error_8h.html\n", - err); + if (c == NULL) { + fprintf(stderr, "Failed to create MPD connection object\n"); + return NULL; + } + + if (mpd_connection_get_error(c) != MPD_ERROR_SUCCESS) { + fprintf(stderr, "Error connecting to MPD (%s:%u): %s\n", host, port, + mpd_connection_get_error_message(c)); mpd_connection_free(c); - return 0; + return NULL; } #ifdef PASS const char *pass = PASS; if (mpd_run_password(c, pass) == false) { - printf("%s\n", "Bad password"); + fprintf(stderr, "Bad password\n"); mpd_connection_free(c); - return 0; + return NULL; } #endif D(printf("%s %s:%u\n", "Connected to", host, port)); @@ -64,7 +67,7 @@ int main(int argc, char *argv[]) { D(printf("Using playlist: %s\n", playlist)); struct mpd_connection *c = conn(); - if (c == 0) + if (c == NULL) return -1; struct mpd_song *curr = mpd_run_current_song(c); diff --git a/mpd/mpd_add_to_queue.c b/mpd/mpd_add_to_queue.c @@ -1,8 +1,8 @@ #include <mpd/client.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdbool.h> #include <unistd.h> // #define DEBUG @@ -25,7 +25,7 @@ struct mpd_connection *conn() { if (host == NULL || strlen(host) == 0) { host = DEFAULT_HOST; } - + unsigned port = DEFAULT_PORT; const char *port_str = getenv("MPD_PORT"); if (port_str != NULL && strlen(port_str) > 0) { @@ -40,15 +40,16 @@ struct mpd_connection *conn() { enum mpd_error err = mpd_connection_get_error(c); if (err != 0) { - fprintf(stderr, "Error connecting to MPD (%s:%u): %s (code: %u)\n", - host, port, mpd_connection_get_error_message(c), err); + fprintf(stderr, "Error connecting to MPD (%s:%u): %s (code: %u)\n", host, + port, mpd_connection_get_error_message(c), err); mpd_connection_free(c); return NULL; } #ifdef PASS const char *pass = PASS; if (mpd_run_password(c, pass) == false) { - fprintf(stderr, "MPD authentication failed: %s\n", mpd_connection_get_error_message(c)); + fprintf(stderr, "MPD authentication failed: %s\n", + mpd_connection_get_error_message(c)); mpd_connection_free(c); return NULL; } @@ -58,7 +59,7 @@ struct mpd_connection *conn() { int main(int argc, char *argv[]) { FILE *input = stdin; - + if (argc > 1) { input = fopen(argv[1], "r"); if (input == NULL) { @@ -66,12 +67,14 @@ int main(int argc, char *argv[]) { return 1; } } else if (isatty(STDIN_FILENO)) { - fprintf(stderr, "Reading from stdin... (Press Ctrl+D to finish, or provide a filename as argument)\n"); + fprintf(stderr, "Reading from stdin... (Press Ctrl+D to finish, or provide " + "a filename as argument)\n"); } struct mpd_connection *c = conn(); if (c == NULL) { - if (input != stdin) fclose(input); + if (input != stdin) + fclose(input); return -1; } @@ -91,23 +94,29 @@ int main(int argc, char *argv[]) { read_bytes--; } - if (read_bytes == 0) continue; - if (line[0] == '#') continue; + if (read_bytes == 0) + continue; + if (line[0] == '#') + continue; if (!mpd_run_add(c, line)) { - fprintf(stderr, "Failed to add track: %s (Error: %s)\n", line, mpd_connection_get_error_message(c)); + fprintf(stderr, "Failed to add track: %s (Error: %s)\n", line, + mpd_connection_get_error_message(c)); failed_count++; - if (!mpd_connection_clear_error(c)) break; + if (!mpd_connection_clear_error(c)) + break; } else { printf("Added: %s\n", line); added_count++; } } - printf("\nSummary: Added %d tracks, failed to add %d tracks.\n", added_count, failed_count); + printf("\nSummary: Added %d tracks, failed to add %d tracks.\n", added_count, + failed_count); free(line); - if (input != stdin) fclose(input); + if (input != stdin) + fclose(input); mpd_connection_free(c); return 0; diff --git a/mpd/mpd_edit_queue.c b/mpd/mpd_edit_queue.c @@ -6,6 +6,7 @@ #include <unistd.h> #include <sys/wait.h> #include <ctype.h> +#include <fcntl.h> #define DEFAULT_HOST "localhost" #define DEFAULT_PORT 6600 @@ -46,31 +47,10 @@ int main() { struct mpd_connection *c = conn(); if (c == NULL) return 1; - struct mpd_status *status = mpd_run_status(c); - if (status == NULL) { - fprintf(stderr, "Error getting status: %s\n", mpd_connection_get_error_message(c)); - mpd_connection_free(c); - return 1; - } - enum mpd_state state = mpd_status_get_state(status); - int current_pos = mpd_status_get_song_pos(status); - int total_queue_len = mpd_status_get_queue_length(status); - mpd_status_free(status); - - char *current_file = NULL; - if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { - struct mpd_song *song = mpd_run_current_song(c); - if (song != NULL) { - current_file = strdup(mpd_song_get_uri(song)); - mpd_song_free(song); - } - } - char tmp_template[] = "/tmp/mpd_queue_XXXXXX"; int fd = mkstemp(tmp_template); if (fd == -1) { perror("mkstemp"); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } @@ -80,7 +60,6 @@ int main() { perror("fdopen"); close(fd); unlink(tmp_template); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } @@ -89,7 +68,6 @@ int main() { fprintf(stderr, "Error sending queue request\n"); fclose(f); unlink(tmp_template); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } @@ -107,7 +85,6 @@ int main() { if (mpd_connection_get_error(c) != MPD_ERROR_SUCCESS) { fprintf(stderr, "Error reading queue: %s\n", mpd_connection_get_error_message(c)); unlink(tmp_template); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } @@ -120,7 +97,6 @@ int main() { if (pid == -1) { perror("fork"); unlink(tmp_template); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } else if (pid == 0) { @@ -132,11 +108,19 @@ int main() { int wstatus; waitpid(pid, &wstatus, 0); - f = fopen(tmp_template, "r"); + /* Re-open temp file with O_NOFOLLOW to prevent symlink attacks */ + int fd2 = open(tmp_template, O_RDONLY | O_NOFOLLOW); + if (fd2 == -1) { + perror("open"); + unlink(tmp_template); + mpd_connection_free(c); + return 1; + } + f = fdopen(fd2, "r"); if (f == NULL) { - perror("fopen"); + perror("fdopen"); + close(fd2); unlink(tmp_template); - if (current_file) free(current_file); mpd_connection_free(c); return 1; } @@ -146,6 +130,10 @@ int main() { char *line = NULL; size_t len = 0; ssize_t read_bytes; + char *current_file = NULL; + enum mpd_state state; + int current_pos; + int total_queue_len; int new_pos = -1; while ((read_bytes = getline(&line, &len, f)) != -1) { @@ -164,13 +152,35 @@ int main() { perror("strdup"); goto cleanup; } - - if (current_file && strcmp(new_tracks[track_count], current_file) == 0 && new_pos == -1) { - new_pos = track_count; - } track_count++; } + /* Re-query MPD state after editor to avoid stale data */ + struct mpd_status *status = mpd_run_status(c); + if (status == NULL) { + fprintf(stderr, "Error getting status: %s\n", mpd_connection_get_error_message(c)); + goto cleanup; + } + state = mpd_status_get_state(status); + current_pos = mpd_status_get_song_pos(status); + total_queue_len = mpd_status_get_queue_length(status); + mpd_status_free(status); + + if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { + struct mpd_song *song = mpd_run_current_song(c); + if (song != NULL) { + current_file = strdup(mpd_song_get_uri(song)); + mpd_song_free(song); + } + } + + /* Compute new_pos from fresh current_file */ + for (int i = 0; i < track_count; i++) { + if (current_file && strcmp(new_tracks[i], current_file) == 0 && new_pos == -1) { + new_pos = i; + } + } + if (!mpd_command_list_begin(c, false)) { fprintf(stderr, "Failed to start command list: %s\n", mpd_connection_get_error_message(c)); goto cleanup; diff --git a/mpd/mpd_update_library.c b/mpd/mpd_update_library.c @@ -1,153 +1,169 @@ +#include <ctype.h> #include <mpd/client.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdbool.h> #include <unistd.h> -#include <ctype.h> #define DEFAULT_HOST "localhost" #define DEFAULT_PORT 6600 #define DEFAULT_BACKOFF 5 struct mpd_connection *conn() { - const char *host = getenv("MPD_HOST"); - if (host == NULL || strlen(host) == 0) host = DEFAULT_HOST; - - unsigned port = DEFAULT_PORT; - const char *port_str = getenv("MPD_PORT"); - if (port_str != NULL && strlen(port_str) > 0) port = (unsigned)atoi(port_str); - - struct mpd_connection *c = mpd_connection_new(host, port, 0); - if (c == NULL) { - fprintf(stderr, "Failed to create MPD connection object\n"); - return NULL; - } - - if (mpd_connection_get_error(c) != MPD_ERROR_SUCCESS) { - fprintf(stderr, "Error connecting to MPD (%s:%u): %s\n", host, port, mpd_connection_get_error_message(c)); - mpd_connection_free(c); - return NULL; - } - return c; + const char *host = getenv("MPD_HOST"); + if (host == NULL || strlen(host) == 0) + host = DEFAULT_HOST; + + unsigned port = DEFAULT_PORT; + const char *port_str = getenv("MPD_PORT"); + if (port_str != NULL && strlen(port_str) > 0) + port = (unsigned)atoi(port_str); + + struct mpd_connection *c = mpd_connection_new(host, port, 0); + if (c == NULL) { + fprintf(stderr, "Failed to create MPD connection object\n"); + return NULL; + } + + if (mpd_connection_get_error(c) != MPD_ERROR_SUCCESS) { + fprintf(stderr, "Error connecting to MPD (%s:%u): %s\n", host, port, + mpd_connection_get_error_message(c)); + mpd_connection_free(c); + return NULL; + } + return c; } -char* trim_whitespace(char* str) { - char *end; - while(isspace((unsigned char)*str)) str++; - if(*str == 0) return str; - end = str + strlen(str) - 1; - while(end > str && isspace((unsigned char)*end)) end--; - end[1] = '\0'; +char *trim_whitespace(char *str) { + char *end; + while (isspace((unsigned char)*str)) + str++; + if (*str == 0) return str; + end = str + strlen(str) - 1; + while (end > str && isspace((unsigned char)*end)) + end--; + end[1] = '\0'; + return str; } int main(int argc, char *argv[]) { - if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { - printf("Usage: %s [input_file]\n", argv[0]); - return 0; - } - - FILE *input = stdin; - if (argc > 1) { - input = fopen(argv[1], "r"); - if (input == NULL) { - perror("Error opening file"); - return 1; - } - } else if (isatty(STDIN_FILENO)) { - fprintf(stderr, "Reading from stdin... (Press Ctrl+D to finish)\n"); - } - - char **top_dirs = NULL; - int dir_count = 0; - char *line = NULL; - size_t len = 0; - ssize_t read_bytes; - - while ((read_bytes = getline(&line, &len, input)) != -1) { - char *trimmed = trim_whitespace(line); - if (strlen(trimmed) == 0 || trimmed[0] == '#') continue; - - char *slash = strchr(trimmed, '/'); - if (slash != NULL) *slash = '\0'; - - bool found = false; - for (int i = 0; i < dir_count; i++) { - if (strcmp(top_dirs[i], trimmed) == 0) { - found = true; - break; - } - } - - if (!found) { - char **temp_dirs = realloc(top_dirs, sizeof(char *) * (dir_count + 1)); - if (temp_dirs == NULL) { - perror("realloc"); - break; - } - top_dirs = temp_dirs; - top_dirs[dir_count] = strdup(trimmed); - if (top_dirs[dir_count] == NULL) { - perror("strdup"); - break; - } - dir_count++; - } - } - free(line); - if (input != stdin) fclose(input); - - if (dir_count == 0) { - printf("No directories found in input.\n"); - for (int i = 0; i < dir_count; i++) free(top_dirs[i]); - free(top_dirs); - return 0; - } - - printf("Found %d unique top-level directories to update.\n", dir_count); - - struct mpd_connection *c = conn(); - if (c == NULL) { - for (int i = 0; i < dir_count; i++) free(top_dirs[i]); - free(top_dirs); - return 1; + if (argc > 1 && + (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { + printf("Usage: %s [input_file]\n", argv[0]); + return 0; + } + + FILE *input = stdin; + if (argc > 1) { + input = fopen(argv[1], "r"); + if (input == NULL) { + perror("Error opening file"); + return 1; } - - int backoff = DEFAULT_BACKOFF; - const char *backoff_str = getenv("MPD_UPDATE_BACKOFF"); - if (backoff_str != NULL && strlen(backoff_str) > 0) backoff = atoi(backoff_str); - - int updated_count = 0; - int error_count = 0; - + } else if (isatty(STDIN_FILENO)) { + fprintf(stderr, "Reading from stdin... (Press Ctrl+D to finish)\n"); + } + + char **top_dirs = NULL; + int dir_count = 0; + char *line = NULL; + size_t len = 0; + ssize_t read_bytes; + + while ((read_bytes = getline(&line, &len, input)) != -1) { + char *trimmed = trim_whitespace(line); + if (strlen(trimmed) == 0 || trimmed[0] == '#') + continue; + + char *slash = strchr(trimmed, '/'); + if (slash != NULL) + *slash = '\0'; + + bool found = false; for (int i = 0; i < dir_count; i++) { - printf("Updating: '%s'\n", top_dirs[i]); - unsigned job_id = mpd_run_update(c, top_dirs[i]); - if (job_id == 0) { - fprintf(stderr, " ✗ Failed: %s\n", mpd_connection_get_error_message(c)); - error_count++; - mpd_connection_clear_error(c); - } else { - printf(" ✓ Success (Job ID: %u)\n", job_id); - updated_count++; - } - if (i < dir_count - 1 && backoff > 0) sleep(backoff); + if (strcmp(top_dirs[i], trimmed) == 0) { + found = true; + break; + } } - printf("Update completed!\n"); - printf("Total directories processed: %d\n", dir_count); - printf("Successfully updated: %d\n", updated_count); - printf("Errors: %d\n", error_count); - - struct mpd_status *status = mpd_run_status(c); - if (status != NULL) { - if (mpd_status_get_update_id(status) > 0) printf("MPD is currently updating...\n"); - mpd_status_free(status); + if (!found) { + char **temp_dirs = realloc(top_dirs, sizeof(char *) * (dir_count + 1)); + if (temp_dirs == NULL) { + perror("realloc"); + break; + } + top_dirs = temp_dirs; + top_dirs[dir_count] = strdup(trimmed); + if (top_dirs[dir_count] == NULL) { + perror("strdup"); + break; + } + dir_count++; } - - for (int i = 0; i < dir_count; i++) free(top_dirs[i]); + } + free(line); + if (input != stdin) + fclose(input); + + if (dir_count == 0) { + printf("No directories found in input.\n"); + for (int i = 0; i < dir_count; i++) + free(top_dirs[i]); free(top_dirs); - mpd_connection_free(c); return 0; + } + + printf("Found %d unique top-level directories to update.\n", dir_count); + + struct mpd_connection *c = conn(); + if (c == NULL) { + for (int i = 0; i < dir_count; i++) + free(top_dirs[i]); + free(top_dirs); + return 1; + } + + int backoff = DEFAULT_BACKOFF; + const char *backoff_str = getenv("MPD_UPDATE_BACKOFF"); + if (backoff_str != NULL && strlen(backoff_str) > 0) + backoff = atoi(backoff_str); + + int updated_count = 0; + int error_count = 0; + + for (int i = 0; i < dir_count; i++) { + printf("Updating: '%s'\n", top_dirs[i]); + unsigned job_id = mpd_run_update(c, top_dirs[i]); + if (job_id == 0) { + fprintf(stderr, " ✗ Failed: %s\n", mpd_connection_get_error_message(c)); + error_count++; + mpd_connection_clear_error(c); + } else { + printf(" ✓ Success (Job ID: %u)\n", job_id); + updated_count++; + } + if (i < dir_count - 1 && backoff > 0) + sleep(backoff); + } + + printf("Update completed!\n"); + printf("Total directories processed: %d\n", dir_count); + printf("Successfully updated: %d\n", updated_count); + printf("Errors: %d\n", error_count); + + struct mpd_status *status = mpd_run_status(c); + if (status != NULL) { + if (mpd_status_get_update_id(status) > 0) + printf("MPD is currently updating...\n"); + mpd_status_free(status); + } + + for (int i = 0; i < dir_count; i++) + free(top_dirs[i]); + free(top_dirs); + mpd_connection_free(c); + return 0; } diff --git a/mpd/mpd_update_queue.c b/mpd/mpd_update_queue.c @@ -48,35 +48,14 @@ int main(int argc, char *argv[]) { return 1; } - struct mpd_status *status = mpd_run_status(c); - if (status == NULL) { - fprintf(stderr, "Error getting status: %s\n", mpd_connection_get_error_message(c)); - fclose(input); - mpd_connection_free(c); - return 1; - } - - enum mpd_state state = mpd_status_get_state(status); - int current_pos = mpd_status_get_song_pos(status); - int total_queue_len = mpd_status_get_queue_length(status); - mpd_status_free(status); - - char *current_file = NULL; - if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { - struct mpd_song *song = mpd_run_current_song(c); - if (song != NULL) { - current_file = strdup(mpd_song_get_uri(song)); - mpd_song_free(song); - } - } - char **new_tracks = NULL; int track_count = 0; char *line = NULL; size_t len = 0; ssize_t read_bytes; - int new_pos = -1; + char *current_file = NULL; + /* Read input file first, before querying MPD state */ while ((read_bytes = getline(&line, &len, input)) != -1) { if (read_bytes > 0 && line[read_bytes - 1] == '\n') line[read_bytes - 1] = '\0'; if (read_bytes > 1 && line[read_bytes - 2] == '\r') line[read_bytes - 2] = '\0'; @@ -94,13 +73,36 @@ int main(int argc, char *argv[]) { perror("strdup"); goto cleanup; } - - if (current_file && strcmp(new_tracks[track_count], current_file) == 0 && new_pos == -1) { - new_pos = track_count; - } track_count++; } + /* Re-query MPD state before building command list */ + struct mpd_status *status = mpd_run_status(c); + if (status == NULL) { + fprintf(stderr, "Error getting status: %s\n", mpd_connection_get_error_message(c)); + goto cleanup; + } + enum mpd_state state = mpd_status_get_state(status); + int current_pos = mpd_status_get_song_pos(status); + int total_queue_len = mpd_status_get_queue_length(status); + mpd_status_free(status); + + if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { + struct mpd_song *song = mpd_run_current_song(c); + if (song != NULL) { + current_file = strdup(mpd_song_get_uri(song)); + mpd_song_free(song); + } + } + + /* Compute new_pos from fresh current_file */ + int new_pos = -1; + for (int i = 0; i < track_count; i++) { + if (current_file && strcmp(new_tracks[i], current_file) == 0 && new_pos == -1) { + new_pos = i; + } + } + if (!mpd_command_list_begin(c, false)) { fprintf(stderr, "Failed to start command list: %s\n", mpd_connection_get_error_message(c)); goto cleanup;