Merge pull request #37 from m1ngsama/fix/deadlock-uaf-logrotate-tail

fix: deadlock, use-after-free, log rotation, and tail parsing
This commit is contained in:
m1ngsama 2026-04-19 18:30:45 +08:00 committed by GitHub
commit 450f1828fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 30 additions and 7 deletions

View file

@ -22,6 +22,7 @@
#define MAX_EXEC_COMMAND_LEN 1024 #define MAX_EXEC_COMMAND_LEN 1024
#define MAX_CLIENTS 64 #define MAX_CLIENTS 64
#define LOG_FILE "messages.log" #define LOG_FILE "messages.log"
#define MAX_LOG_SIZE (10 * 1024 * 1024) /* 10 MiB */
#define HOST_KEY_FILE "host_key" #define HOST_KEY_FILE "host_key"
#define TNT_DEFAULT_STATE_DIR "." #define TNT_DEFAULT_STATE_DIR "."

View file

@ -227,7 +227,16 @@ int message_save(const message_t *msg) {
rc = -1; rc = -1;
} }
/* Rotate if the log exceeds MAX_LOG_SIZE */
long file_size = ftell(fp);
fclose(fp); fclose(fp);
if (file_size > MAX_LOG_SIZE) {
char backup_path[PATH_MAX + 4];
snprintf(backup_path, sizeof(backup_path), "%s.1", log_path);
rename(log_path, backup_path);
}
pthread_mutex_unlock(&g_message_file_lock); pthread_mutex_unlock(&g_message_file_lock);
return rc; return rc;
} }

View file

@ -895,7 +895,7 @@ static int parse_tail_count(const char *args, int *count) {
return 0; return 0;
} }
if (strncmp(args, "-n", 2) == 0 && isspace((unsigned char)args[2])) { if (strncmp(args, "-n", 2) == 0) {
args += 2; args += 2;
while (*args && isspace((unsigned char)*args)) { while (*args && isspace((unsigned char)*args)) {
args++; args++;
@ -1170,21 +1170,28 @@ static void execute_command(client_t *client) {
" w <username> <message>\n"); " w <username> <message>\n");
} else { } else {
bool found = false; bool found = false;
client_t *target = NULL;
pthread_rwlock_rdlock(&g_room->lock); pthread_rwlock_rdlock(&g_room->lock);
for (int i = 0; i < g_room->client_count; i++) { for (int i = 0; i < g_room->client_count; i++) {
if (strcmp(g_room->clients[i]->username, target_name) == 0) { if (strcmp(g_room->clients[i]->username, target_name) == 0) {
char whisper[MAX_MESSAGE_LEN]; target = g_room->clients[i];
snprintf(whisper, sizeof(whisper), client_addref(target);
"\r\n\033[35m[whisper from %s]: %s\033[0m\r\n",
client->username, rest);
client_send(g_room->clients[i], whisper, strlen(whisper));
g_room->clients[i]->redraw_pending = true;
found = true; found = true;
break; break;
} }
} }
pthread_rwlock_unlock(&g_room->lock); pthread_rwlock_unlock(&g_room->lock);
if (target) {
char whisper[MAX_MESSAGE_LEN];
snprintf(whisper, sizeof(whisper),
"\r\n\033[35m[whisper from %s]: %s\033[0m\r\n",
client->username, rest);
client_send(target, whisper, strlen(whisper));
target->redraw_pending = true;
client_release(target);
}
if (found) { if (found) {
buffer_appendf(output, sizeof(output), &pos, buffer_appendf(output, sizeof(output), &pos,
"Whisper sent to %s\n", target_name); "Whisper sent to %s\n", target_name);
@ -1648,6 +1655,12 @@ cleanup:
release_ip_connection(client->client_ip); release_ip_connection(client->client_ip);
/* Remove channel callbacks before releasing refs to prevent use-after-free
* if a callback fires between the two releases. */
if (client->channel && client->channel_cb) {
ssh_remove_channel_callbacks(client->channel, client->channel_cb);
}
/* Release the callback reference (paired with addref before install_client_channel_callbacks) */ /* Release the callback reference (paired with addref before install_client_channel_callbacks) */
client_release(client); client_release(client);