fix(ui,db): harden NULL handling and resource lifecycle across UI and SQLite

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:
2025-11-05 14:39:10 +03:00
parent f8826b7c79
commit a9c21ce487
8 changed files with 203 additions and 25 deletions

View File

@@ -98,17 +98,23 @@ log_database_init(ProfAccount* account)
auto_char char* filename = _get_db_filename(account);
if (!filename) {
sqlite3_shutdown();
return FALSE;
}
ret = sqlite3_open(filename, &g_chatlog_database);
if (ret != SQLITE_OK) {
const char* err_msg = sqlite3_errmsg(g_chatlog_database);
const char* err_msg = g_chatlog_database ? sqlite3_errmsg(g_chatlog_database) : "(no handle)";
log_error("Error opening SQLite database: %s", err_msg);
if (g_chatlog_database) {
sqlite3_close(g_chatlog_database);
g_chatlog_database = NULL;
}
sqlite3_shutdown();
return FALSE;
}
char* err_msg;
char* err_msg = NULL;
int db_version = _get_db_version();
if (db_version == latest_version) {
@@ -216,6 +222,11 @@ out:
} else {
log_error("Unknown SQLite error in log_database_init().");
}
if (g_chatlog_database) {
sqlite3_close(g_chatlog_database);
g_chatlog_database = NULL;
}
sqlite3_shutdown();
return FALSE;
}
@@ -224,7 +235,10 @@ log_database_close(void)
{
log_debug("log_database_close() called");
if (g_chatlog_database) {
sqlite3_close(g_chatlog_database);
int rc = sqlite3_close_v2(g_chatlog_database);
if (rc != SQLITE_OK) {
log_warning("sqlite3_close_v2 returned %d; database may still have active statements.", rc);
}
sqlite3_shutdown();
g_chatlog_database = NULL;
}
@@ -281,8 +295,15 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
{
sqlite3_stmt* stmt = NULL;
const Jid* myjid = connection_get_jid();
if (!myjid->str)
return NULL;
// Always return a valid ProfMessage to avoid NULL dereferences in callers
ProfMessage* msg = message_init();
if (!myjid || !myjid->str) {
// If caller requested the last message and we have no context, fall back to now
if (is_last) {
msg->timestamp = g_date_time_new_now_utc();
}
return msg;
}
const char* order = is_last ? "DESC" : "ASC";
auto_sqlite char* query = sqlite3_mprintf("SELECT `archive_id`, `timestamp` FROM `ChatLogs` WHERE "
@@ -293,17 +314,21 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
if (!query) {
log_error("Could not allocate memory for SQL query in log_database_get_limits_info()");
return NULL;
if (is_last) {
msg->timestamp = g_date_time_new_now_utc();
}
return msg;
}
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
log_error("Unknown SQLite error in log_database_get_last_info().");
return NULL;
log_error("Unknown SQLite error in log_database_get_limits_info().");
if (is_last) {
msg->timestamp = g_date_time_new_now_utc();
}
return msg;
}
ProfMessage* msg = message_init();
if (sqlite3_step(stmt) == SQLITE_ROW) {
char* archive_id = (char*)sqlite3_column_text(stmt, 0);
char* date = (char*)sqlite3_column_text(stmt, 1);
@@ -313,6 +338,11 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
}
sqlite3_finalize(stmt);
// If nothing was found and caller expects the last message, provide a sane default
if (!msg->timestamp && is_last) {
msg->timestamp = g_date_time_new_now_utc();
}
return msg;
}