refactor: clean narrowing conversions and harden unsigned arithmetic

Cleanup of the conversion-safety warnings exposed by enabling
-Wconversion / -Wsign-compare in the previous commit, plus guard
clauses at the few places where unsigned arithmetic could actually
misbehave.

Build:
- configure.ac drops -Wno-error=conversion and
  -Wno-error=float-conversion. Only -Wno-error=sign-conversion and
  -Wno-error=sign-compare remain, gating the ~230 sign warnings
  inherited from upstream that will be cleaned up in follow-ups.

Type / conversion fixes (no behaviour change):
- Length-like locals in command/cmd_ac.c, command/cmd_funcs.c,
  pgp/gpg.c, tools/autocomplete.c, tools/parser.c and ui/mucwin.c
  switched from int to size_t / glong (matching strlen /
  g_utf8_strlen return type) so we no longer need an (int) cast and
  loop counters / array sizes stay in their natural unsigned domain.
- g_timer_elapsed / GTimeSpan -> int casts in session.c, iq.c,
  core.c, server_events.c, window.c.
- _win_print_wrapped: indent parameter and local curx/maxx switched
  from size_t to int to match _win_indent / getcurx / getmaxx.
- Port casts (int -> unsigned short) at the libstrophe boundary in
  connection.c and session.c, each preceded by
  g_assert(port >= 0 && port <= UINT16_MAX) so the truncation is
  documented at the call-site.
- curl_off_t / fread size_t results cast at usage in http_upload.c,
  http_download.c, omemo/crypto.c.
- strtoul results cast to uint32_t in xmpp/omemo.c and omemo/omemo.c
  where device/prekey IDs are genuinely 32-bit.
- config/color.c: fg/bg/palette indices switched to `short`
  end-to-end (find_col, color_hash, find_closest_col,
  _color_pair_cache_get, cache.pairs), so the ncurses init_pair
  boundary needs at most one (short)i cast for the cache index. Also
  TODO-noted: init_extended_pair is needed for >15-bit palettes.
- xmpp/avatar.c: float arithmetic explicitly casts its int operands.
- tests/functionaltests/proftest.c: read() result handling uses
  size_t for the accumulator, _read_output returns ssize_t, and the
  buffer-shift check happens before space subtraction so the
  expression cannot underflow.

Real-risk guard clauses (the part that actually fixes bugs):
- src/ui/statusbar.c _tabs_width: `end > opened_tabs - 1` rewritten
  as `end < opened_tabs` so opened_tabs == 0 no longer underflows.
- src/ui/statusbar.c _status_bar_draw_extended_tabs: the mirror
  comparison rewritten as `end >= opened_tabs`.
- src/ui/statusbar.c status_bar_draw: replaced
  `MAX(0, getmaxx - (int)_tabs_width)` with an explicit precheck
  before subtraction.
- src/omemo/omemo.c prekey selection: prekey_index is now uint32_t
  and randomized into an unsigned buffer, so modulo with prekeys_len
  cannot yield a negative index for g_list_nth_data.
- src/omemo/crypto.c omemo_decrypt_func: PKCS#5/PKCS#7 unpadding
  reads `plaintext[plaintext_len - 1]`, which would underflow on a
  malformed empty ciphertext and read past the heap buffer. Reject
  plaintext_len == 0 before the padding peek and validate the
  padding byte against the buffer length before the unpad loop.
  Initialise plaintext = NULL so the early `goto out` cannot free
  uninitialised memory.
- src/ui/inputwin.c (4 mbrlen sites) and src/ui/window.c
  _win_print_wrapped: mbrlen() returns 0 for the null wide
  character. The existing checks rejected (size_t)-1 / -2 but
  treated 0 as a valid step, so the surrounding loops would either
  advance by SIZE_MAX (i += ch_len - 1) or spin in place
  (word_pos += 0 forever). Add `|| ch_len == 0` to each guard;
  inside the spell-check word-emission loop also fall back to a
  one-byte advance.
- Defensive `len > 0 ? len - 1 : 0` prechecks at the strlen-based
  g_strndup / loop sites in ui/console.c, plugins/c_api.c and
  plugins/python_plugins.c.
This commit is contained in:
2026-04-27 15:03:52 +03:00
parent 5459e78e82
commit 9feff00ead
28 changed files with 193 additions and 133 deletions

View File

@@ -290,7 +290,7 @@ autocomplete_complete(Autocomplete ac, const gchar* search_str, gboolean quote,
static char*
_autocomplete_param_common(const char* const input, char* command, autocomplete_func func, Autocomplete ac, gboolean quote, gboolean previous, void* context)
{
int len;
size_t len;
auto_gchar gchar* command_cpy = g_strdup_printf("%s ", command);
if (!command_cpy) {
return NULL;
@@ -299,14 +299,14 @@ _autocomplete_param_common(const char* const input, char* command, autocomplete_
len = strlen(command_cpy);
if (strncmp(input, command_cpy, len) == 0) {
int inp_len = strlen(input);
size_t inp_len = strlen(input);
auto_char char* found = NULL;
auto_char char* prefix = malloc(inp_len + 1);
if (!prefix) {
return NULL;
}
for (int i = len; i < inp_len; i++) {
for (size_t i = len; i < inp_len; i++) {
prefix[i - len] = input[i];
}

View File

@@ -54,7 +54,7 @@ _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot
unsigned int dlperc = 0;
if (dltotal != 0) {
dlperc = (100 * dlnow) / dltotal;
dlperc = (unsigned int)((100 * dlnow) / dltotal);
}
if (!download->silent) {

View File

@@ -62,7 +62,7 @@ _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot
unsigned int ulperc = 0;
if (ultotal != 0) {
ulperc = (100 * ulnow) / ultotal;
ulperc = (unsigned int)((100 * ulnow) / ultotal);
}
gchar* msg = NULL;

View File

@@ -28,7 +28,7 @@ _parse_args_helper(const char* const inp, int min, int max, gboolean* result, gb
auto_char char* copy = strdup(inp);
g_strstrip(copy);
int inp_size = g_utf8_strlen(copy, -1);
glong inp_size = g_utf8_strlen(copy, -1);
gboolean in_token = FALSE;
gboolean in_freetext = FALSE;
gboolean in_quotes = FALSE;
@@ -37,7 +37,7 @@ _parse_args_helper(const char* const inp, int min, int max, gboolean* result, gb
GSList* tokens = NULL;
// add tokens to GSList
for (int i = 0; i < inp_size; i++) {
for (glong i = 0; i < inp_size; i++) {
gchar* curr_ch = g_utf8_offset_to_pointer(copy, i);
gunichar curr_uni = g_utf8_get_char(curr_ch);
@@ -231,10 +231,10 @@ gchar**
parse_args_as_one(const char* const inp, int min, int max, gboolean* result)
{
gchar** args = g_malloc0(2 * sizeof(*args));
int length = g_utf8_strlen(inp, -1);
glong length = g_utf8_strlen(inp, -1);
gchar* space = g_utf8_strchr(inp, length, ' ');
if (space) {
int sub_length = g_utf8_strlen(space, -1);
glong sub_length = g_utf8_strlen(space, -1);
if (sub_length > 1) {
args[0] = g_strdup(space + 1);
*result = TRUE;
@@ -253,14 +253,14 @@ parse_args_as_one(const char* const inp, int min, int max, gboolean* result)
int
count_tokens(const char* const string)
{
int length = g_utf8_strlen(string, -1);
glong length = g_utf8_strlen(string, -1);
gboolean in_quotes = FALSE;
int num_tokens = 0;
// include first token
num_tokens++;
for (int i = 0; i < length; i++) {
for (glong i = 0; i < length; i++) {
gchar* curr_ch = g_utf8_offset_to_pointer(string, i);
gunichar curr_uni = g_utf8_get_char(curr_ch);
@@ -298,14 +298,14 @@ char*
get_start(const char* const string, int tokens)
{
GString* result = g_string_new("");
int length = g_utf8_strlen(string, -1);
glong length = g_utf8_strlen(string, -1);
gboolean in_quotes = FALSE;
int num_tokens = 0;
// include first token
num_tokens++;
for (int i = 0; i < length; i++) {
for (glong i = 0; i < length; i++) {
gchar* curr_ch = g_utf8_offset_to_pointer(string, i);
gunichar curr_uni = g_utf8_get_char(curr_ch);