fix(ui,db): harden NULL handling and resource lifecycle across UI and SQLite
Some checks failed
CI Code / Linux (arch) (pull_request) Failing after 20s
CI Code / Check spelling (pull_request) Successful in 24s
CI Code / Check coding style (pull_request) Failing after 36s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m27s
CI Code / Linux (debian) (pull_request) Successful in 17m51s
Some checks failed
CI Code / Linux (arch) (pull_request) Failing after 20s
CI Code / Check spelling (pull_request) Successful in 24s
CI Code / Check coding style (pull_request) Failing after 36s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m27s
CI Code / Linux (debian) (pull_request) Successful in 17m51s
ui/window: fix subwindow lifecycle (safe delwin on recreate), clamp widths, add fallback timestamp when loading history to avoid NULL deref ui/buffer: add GSList bounds checks; assert non-NULL timestamps when creating entries ui/titlebar: guard newwin failures; make draw/resize/free no-ops when window is NULL ui/statusbar: guard window creation/resize/close; clamp columns; fallback display name if JID parsing fails ui/inputwin: check newpad result; guard resize/getters/close on NULL; safe delwin ui/chatwin: guard buffer_get_entry/time before ISO8601 formatting ui/window_list: validate win_create_* results; don’t insert NULL windows; fix barejid leak in wins_get_by_string db/database: ensure cleanup on sqlite init/open failures; use sqlite3_close_v2 and warn if busy; always return a ProfMessage from log_database_get_limits_info and set current UTC timestamp when is_last with no row; initialize err_msg and free consistently; improve error messages Prevents crashes from NULL dereferences (e.g., during MAM history) and closes small leaks; improves robustness under OOM and allocation failures.
This commit is contained in:
@@ -80,7 +80,15 @@ win_roster_cols(void)
|
||||
{
|
||||
int roster_win_percent = prefs_get_roster_size();
|
||||
int cols = getmaxx(stdscr);
|
||||
return CEILING((((double)cols) / 100) * roster_win_percent);
|
||||
int width = CEILING((((double)cols) / 100) * roster_win_percent);
|
||||
// Clamp to a sane range to avoid zero/full-width pads
|
||||
if (cols > 1) {
|
||||
if (width < 1) width = 1;
|
||||
if (width >= cols) width = cols - 1;
|
||||
} else {
|
||||
width = 1;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -88,7 +96,15 @@ win_occpuants_cols(void)
|
||||
{
|
||||
int occupants_win_percent = prefs_get_occupants_size();
|
||||
int cols = getmaxx(stdscr);
|
||||
return CEILING((((double)cols) / 100) * occupants_win_percent);
|
||||
int width = CEILING((((double)cols) / 100) * occupants_win_percent);
|
||||
// Clamp to a sane range to avoid zero/full-width pads
|
||||
if (cols > 1) {
|
||||
if (width < 1) width = 1;
|
||||
if (width >= cols) width = cols - 1;
|
||||
} else {
|
||||
width = 1;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
static ProfLayout*
|
||||
@@ -142,6 +158,7 @@ win_create_console(void)
|
||||
ProfWin*
|
||||
win_create_chat(const char* const barejid)
|
||||
{
|
||||
assert(barejid != NULL);
|
||||
ProfChatWin* new_win = malloc(sizeof(ProfChatWin));
|
||||
new_win->window.type = WIN_CHAT;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
@@ -173,6 +190,7 @@ win_create_chat(const char* const barejid)
|
||||
ProfWin*
|
||||
win_create_muc(const char* const roomjid)
|
||||
{
|
||||
assert(roomjid != NULL);
|
||||
ProfMucWin* new_win = malloc(sizeof(ProfMucWin));
|
||||
int cols = getmaxx(stdscr);
|
||||
|
||||
@@ -230,6 +248,8 @@ win_create_muc(const char* const roomjid)
|
||||
ProfWin*
|
||||
win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata)
|
||||
{
|
||||
assert(roomjid != NULL);
|
||||
assert(form != NULL);
|
||||
ProfConfWin* new_win = malloc(sizeof(ProfConfWin));
|
||||
new_win->window.type = WIN_CONFIG;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
@@ -248,6 +268,7 @@ win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback
|
||||
ProfWin*
|
||||
win_create_private(const char* const fulljid)
|
||||
{
|
||||
assert(fulljid != NULL);
|
||||
ProfPrivateWin* new_win = malloc(sizeof(ProfPrivateWin));
|
||||
new_win->window.type = WIN_PRIVATE;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
@@ -278,6 +299,8 @@ win_create_xmlconsole(void)
|
||||
ProfWin*
|
||||
win_create_plugin(const char* const plugin_name, const char* const tag)
|
||||
{
|
||||
assert(plugin_name != NULL);
|
||||
assert(tag != NULL);
|
||||
ProfPluginWin* new_win = malloc(sizeof(ProfPluginWin));
|
||||
new_win->window.type = WIN_PLUGIN;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
@@ -294,6 +317,7 @@ win_create_plugin(const char* const plugin_name, const char* const tag)
|
||||
ProfWin*
|
||||
win_create_vcard(vCard* vcard)
|
||||
{
|
||||
assert(vcard != NULL);
|
||||
ProfVcardWin* new_win = malloc(sizeof(ProfVcardWin));
|
||||
new_win->window.type = WIN_VCARD;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
@@ -345,7 +369,7 @@ win_get_title(ProfWin* window)
|
||||
const ProfConfWin* confwin = (ProfConfWin*)window;
|
||||
assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
|
||||
auto_gchar gchar* mucwin_title = mucwin_generate_title(confwin->roomjid, PREF_TITLEBAR_MUC_TITLE);
|
||||
if (confwin->form->modified) {
|
||||
if (confwin->form && confwin->form->modified) {
|
||||
return g_strconcat(mucwin_title, " config *", NULL);
|
||||
}
|
||||
return g_strconcat(mucwin_title, " config", NULL);
|
||||
@@ -553,7 +577,25 @@ win_show_subwin(ProfWin* window)
|
||||
}
|
||||
|
||||
ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
|
||||
// If a subwindow already exists (e.g. repeated call), destroy it to avoid leaks
|
||||
if (layout->subwin) {
|
||||
delwin(layout->subwin);
|
||||
layout->subwin = NULL;
|
||||
}
|
||||
|
||||
// Ensure minimum width to avoid creating a zero-width pad
|
||||
if (subwin_cols <= 0) {
|
||||
subwin_cols = 1;
|
||||
}
|
||||
|
||||
layout->subwin = newpad(PAD_SIZE, subwin_cols);
|
||||
if (layout->subwin == NULL) {
|
||||
// Failed to allocate subwindow; keep base window resized to full width
|
||||
log_error("Failed to create subwindow pad (cols=%d)", subwin_cols);
|
||||
wresize(layout->base.win, PAD_SIZE, cols);
|
||||
win_redraw(window);
|
||||
return;
|
||||
}
|
||||
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
|
||||
wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
|
||||
win_redraw(window);
|
||||
@@ -897,6 +939,11 @@ win_refresh_with_subwin(ProfWin* window)
|
||||
int row_end = screen_mainwin_row_end();
|
||||
ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
|
||||
|
||||
// Safety: if subwindow is not active, nothing to refresh
|
||||
if (layout == NULL || layout->subwin == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->type == WIN_MUC) {
|
||||
subwin_cols = win_occpuants_cols();
|
||||
} else if (window->type == WIN_CONSOLE) {
|
||||
@@ -2017,7 +2064,14 @@ win_print_loading_history(ProfWin* window)
|
||||
gboolean is_buffer_empty = buffer_size(window->layout->buffer) == 0;
|
||||
|
||||
if (!is_buffer_empty) {
|
||||
timestamp = buffer_get_entry(window->layout->buffer, 0)->time;
|
||||
ProfBuffEntry* first = buffer_get_entry(window->layout->buffer, 0);
|
||||
if (first && first->time) {
|
||||
timestamp = first->time;
|
||||
} else {
|
||||
// Fallback to current time if entry/time is unavailable
|
||||
timestamp = g_date_time_new_now_local();
|
||||
is_buffer_empty = TRUE; // ensure we unref fallback timestamp below
|
||||
}
|
||||
} else {
|
||||
timestamp = g_date_time_new_now_local();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user