Merge pull request #16 from m1ngsama/fix/memory-safety-and-input-bugs

Fix memory safety bugs and timing side-channel
This commit is contained in:
m1ngsama 2026-04-19 17:38:39 +08:00 committed by GitHub
commit ecaff81384
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 16 additions and 11 deletions

View file

@ -108,16 +108,17 @@ static void buffer_append_bytes(char *buffer, size_t buf_size, size_t *pos,
buffer[*pos] = '\0'; buffer[*pos] = '\0';
} }
/* Constant-time string comparison to prevent timing side-channel attacks */ /* Constant-time string comparison to prevent timing side-channel attacks.
* Always iterates over the full length of the secret (b) to avoid leaking
* its length. When the input (a) is shorter, compares against zero bytes;
* the length mismatch is folded into the result separately. */
static bool constant_time_strcmp(const char *a, const char *b) { static bool constant_time_strcmp(const char *a, const char *b) {
size_t len_a = strlen(a); size_t len_a = strlen(a);
size_t len_b = strlen(b); size_t len_b = strlen(b);
/* Use len_b (the secret) for iteration to avoid leaking its length
* through early termination. The XOR of lengths catches mismatches. */
volatile unsigned char result = (unsigned char)(len_a ^ len_b); volatile unsigned char result = (unsigned char)(len_a ^ len_b);
size_t len = (len_a < len_b) ? len_a : len_b; for (size_t i = 0; i < len_b; i++) {
for (size_t i = 0; i < len; i++) { unsigned char ca = (i < len_a) ? (unsigned char)a[i] : 0;
result |= (unsigned char)((unsigned char)a[i] ^ (unsigned char)b[i]); result |= ca ^ (unsigned char)b[i];
} }
return result == 0; return result == 0;
} }
@ -1768,9 +1769,11 @@ static int client_channel_window_change(ssh_session session, ssh_channel channel
return SSH_ERROR; return SSH_ERROR;
} }
pthread_mutex_lock(&client->io_lock);
client->width = width; client->width = width;
client->height = height; client->height = height;
sanitize_terminal_size(&client->width, &client->height); sanitize_terminal_size(&client->width, &client->height);
pthread_mutex_unlock(&client->io_lock);
client->redraw_pending = true; client->redraw_pending = true;
return SSH_OK; return SSH_OK;
} }
@ -1969,10 +1972,12 @@ static void *bootstrap_client_session(void *arg) {
client_addref(client); client_addref(client);
if (install_client_channel_callbacks(client) < 0) { if (install_client_channel_callbacks(client) < 0) {
client_release(client); /* drop the callback ref */ /* Nullify session/channel ownership so client_release won't
pthread_mutex_destroy(&client->io_lock); * double-free what cleanup_failed_session is about to free. */
pthread_mutex_destroy(&client->ref_lock); client->session = NULL;
free(client); client->channel = NULL;
client_release(client); /* drop the callback ref (2 → 1) */
client_release(client); /* drop the main ref (1 → 0, frees client) */
cleanup_failed_session(session, ctx); cleanup_failed_session(session, ctx);
return NULL; return NULL;
} }

View file

@ -188,7 +188,7 @@ void tui_render_input(client_t *client, const char *input) {
int input_width = utf8_string_width(input); int input_width = utf8_string_width(input);
/* Truncate from start if too long */ /* Truncate from start if too long */
char display[1024]; char display[MAX_MESSAGE_LEN];
strncpy(display, input, sizeof(display) - 1); strncpy(display, input, sizeof(display) - 1);
display[sizeof(display) - 1] = '\0'; display[sizeof(display) - 1] = '\0';