Merge branch 'security-hardening' of https://github.com/z3rco/kitty

This commit is contained in:
Kovid Goyal 2026-04-03 21:24:26 +05:30
commit d8f2d703d7
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
10 changed files with 44 additions and 19 deletions

4
glfw/ibus_glfw.c vendored
View file

@ -287,7 +287,9 @@ get_ibus_address_file_name(void) {
addr = getenv("IBUS_ADDRESS");
int offset = 0;
if (addr && addr[0]) {
memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
size_t len = GLFW_MIN(strlen(addr), sizeof(ans) - 1);
memcpy(ans, addr, len);
ans[len] = '\0';
return ans;
}
const char* disp_num = NULL;

8
glfw/x11_window.c vendored
View file

@ -937,7 +937,9 @@ get_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data)
if (!chunk.sz) break;
if (cap < sz + chunk.sz) {
cap = MAX(cap * 2, sz + 4 * chunk.sz);
buf = realloc(buf, cap * sizeof(buf[0]));
char *new_buf = realloc(buf, cap * sizeof(buf[0]));
if (!new_buf) { free(buf); *data = NULL; cd->get_data(NULL, iter, cd->ctype); return 0; }
buf = new_buf;
}
memcpy(buf + sz, chunk.data, chunk.sz);
sz += chunk.sz;
@ -3636,7 +3638,9 @@ write_chunk(void *object, const char *data, size_t sz) {
if (data) {
if (cw->cap < cw->sz + sz) {
cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);
cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
char *new_buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
if (!new_buf) return false;
cw->buf = new_buf;
}
memcpy(cw->buf + cw->sz, data, sz);
cw->sz += sz;

View file

@ -1940,7 +1940,7 @@ write_to_peer(Peer *peer) {
else if (n < 0) {
if (errno != EINTR) { log_error("write() to peer socket failed with error: %s", strerror(errno)); peer->write.used = 0; peer->write.failed = true; }
} else {
if ((size_t)n > peer->write.used) memmove(peer->write.data, peer->write.data + n, peer->write.used - n);
if ((size_t)n < peer->write.used) memmove(peer->write.data, peer->write.data + n, peer->write.used - n);
peer->write.used -= n;
}
talk_mutex(unlock);

View file

@ -14,6 +14,7 @@
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include <sys/mman.h>
#include <structmember.h>
@ -72,8 +73,8 @@ dealloc_secret(Secret *self) {
static int
__eq__(Secret *a, Secret *b) {
const size_t l = a->secret_len < b->secret_len ? a->secret_len : b->secret_len;
return memcmp(a->secret, b->secret, l) == 0;
if (a->secret_len != b->secret_len) return 0;
return CRYPTO_memcmp(a->secret, b->secret, a->secret_len) == 0;
}
static Py_ssize_t

View file

@ -16,6 +16,7 @@
#include "cross-platform-random.h"
#include <structmember.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
@ -40,7 +41,7 @@ static uint64_t key_hash(KEY_TY k);
#define HASH_FN key_hash
static bool keys_are_equal(CacheKey a, CacheKey b) { return a.hash_keylen == b.hash_keylen && memcmp(a.hash_key, b.hash_key, a.hash_keylen) == 0; }
#define CMPR_FN keys_are_equal
static void free_cache_value(CacheValue *cv) { free(cv->data); cv->data = NULL; free(cv); }
static void free_cache_value(CacheValue *cv) { explicit_bzero(cv->encryption_key, sizeof(cv->encryption_key)); free(cv->data); cv->data = NULL; free(cv); }
static void free_cache_key(CacheKey cv) { free(cv.hash_key); cv.hash_key = NULL; }
#define KEY_DTOR_FN free_cache_key
#define VAL_DTOR_FN free_cache_value

View file

@ -308,11 +308,12 @@ drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz
}
}
if (payload_sz) {
w->drop.accepted_mimes = realloc(w->drop.accepted_mimes, w->drop.accepted_mimes_sz + payload_sz + 2);
if (w->drop.accepted_mimes) {
memcpy(w->drop.accepted_mimes + w->drop.accepted_mimes_sz, payload, payload_sz);
w->drop.accepted_mimes_sz += payload_sz;
}
if (w->drop.accepted_mimes_sz + payload_sz > 1024 * 1024) return;
char *new_buf = realloc(w->drop.accepted_mimes, w->drop.accepted_mimes_sz + payload_sz + 2);
if (!new_buf) return;
w->drop.accepted_mimes = new_buf;
memcpy(w->drop.accepted_mimes + w->drop.accepted_mimes_sz, payload, payload_sz);
w->drop.accepted_mimes_sz += payload_sz;
}
if (!more) {
w->drop.accept_in_progress = false;

View file

@ -2,6 +2,7 @@
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import errno
import hmac
import inspect
import io
import json
@ -572,12 +573,12 @@ def check_bypass(password: str, request_id: str, bypass_data: str) -> bool:
delta = time_ns() - int(timestamp)
if abs(delta) > 5 * 60 * 1e9:
return False
return payload == f'{request_id};{password}'
return hmac.compare_digest(payload, f'{request_id};{password}')
except Exception as err:
log_error(f'Invalid file transmission bypass data received: {err}')
return False
elif protocol == 'sha256':
return (encode_bypass(request_id, password) == bypass_data) if password else False
return hmac.compare_digest(encode_bypass(request_id, password), bypass_data) if password else False
else:
log_error(f'Invalid file transmission bypass data received with protocol: {protocol}')
return False

View file

@ -113,7 +113,7 @@ inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_im
png_read_update_info(png, info);
png_uint_32 rowbytes = png_get_rowbytes(png, info);
d->sz = sizeof(png_byte) * rowbytes * d->height;
d->sz = (size_t)rowbytes * (size_t)d->height;
d->decompressed = malloc(d->sz + 16);
if (d->decompressed == NULL) ABRT(ENOMEM, "Out of memory allocating decompression buffer for PNG");
d->row_pointers = malloc(d->height * sizeof(png_bytep));

View file

@ -2,6 +2,7 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import base64
import hmac
import json
import os
import re
@ -103,6 +104,14 @@ def fnmatch_pattern(pat: str) -> 're.Pattern[str]':
return re.compile(translate(pat))
def _ct_password_lookup(passwords: dict[str, Any], pw: str) -> Any:
result = None
for k, v in passwords.items():
if hmac.compare_digest(k, pw):
result = v
return result
def remote_control_allowed(
pcmd: dict[str, Any], remote_control_passwords: dict[str, Sequence[str]] | None,
window: Optional['Window'], extra_data: dict[str, Any]
@ -110,7 +119,7 @@ def remote_control_allowed(
if not remote_control_passwords:
return True
pw = pcmd.get('password', '')
auth_items = remote_control_passwords.get(pw)
auth_items = _ct_password_lookup(remote_control_passwords, pw)
if pw == '!':
auth_items = None
if auth_items is None:
@ -185,10 +194,10 @@ def is_cmd_allowed(pcmd: dict[str, Any], window: Optional['Window'], from_socket
return False
pa = password_authorizer(auth_items)
return pa.is_cmd_allowed(pcmd, window, from_socket, extra_data)
q = user_password_allowed.get(pw)
q = _ct_password_lookup(user_password_allowed, pw)
if q is not None:
return q
auth_items = get_options().remote_control_password.get(pw)
auth_items = _ct_password_lookup(get_options().remote_control_password, pw)
if auth_items is None:
return None
pa = password_authorizer(auth_items)

View file

@ -182,7 +182,7 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
dest_path = filepath.Clean(dest_path)
mode := func(hdr int64) fs.FileMode {
return fs.FileMode(hdr) & (fs.ModePerm | fs.ModeSetgid | fs.ModeSetuid | fs.ModeSticky)
return fs.FileMode(hdr) & fs.ModePerm
}
set_metadata := func(chmod func(mode fs.FileMode) error, hdr_mode int64) (err error) {
@ -250,6 +250,12 @@ func ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOpti
if !filepath.IsAbs(link_target) {
link_target = filepath.Join(filepath.Dir(dest), link_target)
}
if link_target, err = EvalSymlinksThatExist(link_target); err != nil {
return
}
if !strings.HasPrefix(link_target, needed_prefix) {
continue
}
if err = os.Link(link_target, dest); err != nil {
return
}