fix(ui,db): harden NULL handling, fix CWE-134, optimize iterations
Some checks failed
CI Code / Code Coverage (push) Failing after 10m34s
CI Code / Check spelling (push) Failing after 10m47s
CI Code / Check coding style (push) Failing after 11m4s
CI Code / Linux (ubuntu) (push) Failing after 11m19s
CI Code / Linux (debian) (push) Failing after 11m28s
CI Code / Linux (arch) (push) Failing after 11m38s

security(CWE-134): fix format string injections + add CI check
fix(ui): subwindow lifecycle, newwin/newpad guards, fallback timestamps
fix(db): sqlite cleanup on failures, sqlite3_close_v2
fix(xmpp): queued_messages loop, barejid leak
perf(core): g_hash_table_iter_init instead of g_hash_table_get_keys
refactor(ui): CLAMP macro in _check_subwin_width
test: XEP-0012 and XEP-0045 functional tests

Author: jabber.developer2
Closes #58, #85
This commit is contained in:
2026-02-06 19:27:40 +01:00
parent f8826b7c79
commit 467222d0ca
36 changed files with 656 additions and 238 deletions

View File

@@ -65,6 +65,38 @@ static gboolean _check_available_space_for_db_migration(char* path_to_db);
static const int latest_version = 2;
// Helper: close DB handle (if any), warn on busy, and shutdown SQLite
static void
_db_teardown(const char* ctx)
{
if (g_chatlog_database) {
int rc = sqlite3_close_v2(g_chatlog_database);
if (rc != SQLITE_OK) {
log_warning("sqlite3_close_v2 in %s returned %d; database may still have active statements.",
ctx ? ctx : "db_teardown", rc);
}
g_chatlog_database = NULL;
}
// Safe to call unconditionally; no-op if not initialized.
// See: https://www.sqlite.org/c3ref/initialize.html
sqlite3_shutdown();
}
// Helper: prepare a statement and log a contextual error on failure
static gboolean
_db_prepare_ctx(const char* query, sqlite3_stmt** stmt, const char* ctx)
{
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, stmt, NULL);
if (rc != SQLITE_OK) {
log_error("SQLite error in %s: (error code: %d) %s",
ctx ? ctx : "sqlite3_prepare_v2",
rc,
sqlite3_errmsg(g_chatlog_database));
return FALSE;
}
return TRUE;
}
static char*
_db_strdup(const char* str)
{
@@ -98,17 +130,19 @@ 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);
_db_teardown("log_database_init(open)");
return FALSE;
}
char* err_msg;
char* err_msg = NULL;
int db_version = _get_db_version();
if (db_version == latest_version) {
@@ -216,6 +250,7 @@ out:
} else {
log_error("Unknown SQLite error in log_database_init().");
}
_db_teardown("log_database_init(out)");
return FALSE;
}
@@ -223,11 +258,7 @@ void
log_database_close(void)
{
log_debug("log_database_close() called");
if (g_chatlog_database) {
sqlite3_close(g_chatlog_database);
sqlite3_shutdown();
g_chatlog_database = NULL;
}
_db_teardown("log_database_close");
}
void
@@ -281,8 +312,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 +331,19 @@ 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;
if (!_db_prepare_ctx(query, &stmt, "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 +353,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;
}
@@ -357,9 +402,7 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const gchar*
return DB_RESPONSE_ERROR;
}
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
log_error("SQLite error in log_database_get_previous_chat(): (error code: %d) %s", rc, sqlite3_errmsg(g_chatlog_database));
if (!_db_prepare_ctx(query, &stmt, "log_database_get_previous_chat()")) {
return DB_RESPONSE_ERROR;
}
@@ -503,9 +546,7 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
}
sqlite3_stmt* lmc_stmt = NULL;
if (SQLITE_OK != sqlite3_prepare_v2(g_chatlog_database, replace_check_query, -1, &lmc_stmt, NULL)) {
log_error("SQLite error in _add_to_db() on selecting original message: %s", sqlite3_errmsg(g_chatlog_database));
if (!_db_prepare_ctx(replace_check_query, &lmc_stmt, "_add_to_db(replace_check)")) {
return;
}
@@ -542,8 +583,7 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
}
sqlite3_stmt* stmt;
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, duplicate_check_query, -1, &stmt, NULL)) {
if (_db_prepare_ctx(duplicate_check_query, &stmt, "_add_to_db(duplicate_check)")) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
log_error("Duplicate stanza-id found for the message. stanza_id: %s; archive_id: %s; sender: %s; content: %s", message->id, message->stanzaid, from_jid->barejid, message->plain);
cons_show_error("Got a message with duplicate (server-generated) stanza-id from %s.", from_jid->fulljid);
@@ -599,8 +639,7 @@ _get_db_version(void)
int current_version = -1;
const char* query = "SELECT `version` FROM `DbVersion` LIMIT 1";
sqlite3_stmt* statement;
if (sqlite3_prepare_v2(g_chatlog_database, query, -1, &statement, NULL) == SQLITE_OK) {
if (_db_prepare_ctx(query, &statement, "_get_db_version()")) {
if (sqlite3_step(statement) == SQLITE_ROW) {
current_version = sqlite3_column_int(statement, 0);
}