I’m not a very experienced C developer and I’m attempting to debug a segmentation fault that happens straight after a button click signal handler runs. I’m not sure what exactly is happening since I don’t have a version of GTK with debugging symbols, however I will try building a version of GTK with debugging symbols and see what I can do after posting this topic and edit it accordingly.
After clicking the button, two common segfaults may happen:
* thread #1, name = 'main', stop reason = signal SIGSEGV: address not mapped to object (fault address: 0x10)
* frame #0: 0x00007ffff6d858e9 libgobject-2.0.so.0`___lldb_unnamed_symbol1027 + 4569
frame #1: 0x00007ffff6d859d7 libgobject-2.0.so.0`g_signal_emit_valist + 55
frame #2: 0x00007ffff6d85a94 libgobject-2.0.so.0`g_signal_emit + 148
frame #3: 0x00007ffff74857dd libgtk-4.so.1`___lldb_unnamed_symbol8078 + 141
frame #4: 0x00007ffff6d858d3 libgobject-2.0.so.0`___lldb_unnamed_symbol1027 + 4547
frame #5: 0x00007ffff6d859d7 libgobject-2.0.so.0`g_signal_emit_valist + 55
frame #6: 0x00007ffff6d85a94 libgobject-2.0.so.0`g_signal_emit + 148
frame #7: 0x00007ffff74f654c libgtk-4.so.1`___lldb_unnamed_symbol9112 + 204
frame #8: 0x00007ffff76520ca libgtk-4.so.1`___lldb_unnamed_symbol12739 + 394
frame #9: 0x00007ffff756aa1e libgtk-4.so.1`___lldb_unnamed_symbol10450 + 606
frame #10: 0x00007ffff756b57d libgtk-4.so.1`___lldb_unnamed_symbol10455 + 2317
frame #11: 0x00007ffff748a07d libgtk-4.so.1`___lldb_unnamed_symbol8137 + 61
frame #12: 0x00007ffff7857fd3 libgtk-4.so.1`___lldb_unnamed_symbol18857 + 83
frame #13: 0x00007ffff6d65730 libgobject-2.0.so.0`g_closure_invoke + 320
frame #14: 0x00007ffff6d94896 libgobject-2.0.so.0`___lldb_unnamed_symbol1194 + 2198
frame #15: 0x00007ffff6d85095 libgobject-2.0.so.0`___lldb_unnamed_symbol1027 + 2437
frame #16: 0x00007ffff6d859d7 libgobject-2.0.so.0`g_signal_emit_valist + 55
frame #17: 0x00007ffff6d85a94 libgobject-2.0.so.0`g_signal_emit + 148
frame #18: 0x00007ffff78f3d20 libgtk-4.so.1`___lldb_unnamed_symbol20292 + 144
frame #19: 0x00007ffff78125de libgtk-4.so.1`___lldb_unnamed_symbol18084 + 30
frame #20: 0x00007ffff6e0e199 libglib-2.0.so.0`___lldb_unnamed_symbol2326 + 249
frame #21: 0x00007ffff6e6d3bf libglib-2.0.so.0`___lldb_unnamed_symbol2631 + 831
frame #22: 0x00007ffff6e0d712 libglib-2.0.so.0`g_main_context_iteration + 50
frame #23: 0x00007ffff6fd7ed6 libgio-2.0.so.0`g_application_run + 374
frame #24: 0x000055555555731f Aqueduct`main(argc=1, argv=0x00007fffffffe5f8) at main.c:212:16
frame #25: 0x00007ffff6ac1cd0 libc.so.6`___lldb_unnamed_symbol3264 + 128
frame #26: 0x00007ffff6ac1d8a libc.so.6`__libc_start_main + 138
frame #27: 0x00005555555565b5 Aqueduct`_start + 37
* thread #1, name = 'main', stop reason = signal SIGSEGV: address not mapped to object (fault address: 0x554a56240250)
* frame #0: 0x00007ffff6b378b7 libc.so.6`malloc + 359
frame #1: 0x00007fffe947f506 iris_dri.so`___lldb_unnamed_symbol3930 + 22
frame #2: 0x00007fffe9802793 iris_dri.so`___lldb_unnamed_symbol13004 + 6067
frame #3: 0x00007fffe97efb29 iris_dri.so`___lldb_unnamed_symbol12893 + 1465
frame #4: 0x00007fffe978579a iris_dri.so`___lldb_unnamed_symbol11906 + 1242
frame #5: 0x00007fffe9752344 iris_dri.so`___lldb_unnamed_symbol11546 + 388
frame #6: 0x00007ffff78d52b9 libgtk-4.so.1`___lldb_unnamed_symbol20037 + 1561
frame #7: 0x00007ffff78d02a9 libgtk-4.so.1`___lldb_unnamed_symbol19970 + 201
frame #8: 0x00007ffff78bba17 libgtk-4.so.1`___lldb_unnamed_symbol19676 + 199
frame #9: 0x00007ffff78d4b3b libgtk-4.so.1`___lldb_unnamed_symbol20036 + 1003
frame #10: 0x00007ffff786f6cd libgtk-4.so.1`gsk_renderer_render + 317
frame #11: 0x00007ffff7661b08 libgtk-4.so.1`___lldb_unnamed_symbol12847 + 824
frame #12: 0x00007ffff7662c89 libgtk-4.so.1`___lldb_unnamed_symbol12859 + 25
frame #13: 0x00007ffff77c3589 libgtk-4.so.1`___lldb_unnamed_symbol17052 + 153
frame #14: 0x00007ffff6d858d3 libgobject-2.0.so.0`___lldb_unnamed_symbol1027 + 4547
frame #15: 0x00007ffff6d859d7 libgobject-2.0.so.0`g_signal_emit_valist + 55
frame #16: 0x00007ffff6d85a94 libgobject-2.0.so.0`g_signal_emit + 148
frame #17: 0x00007ffff784f9cc libgtk-4.so.1`___lldb_unnamed_symbol18810 + 284
frame #18: 0x00007ffff6d858d3 libgobject-2.0.so.0`___lldb_unnamed_symbol1027 + 4547
frame #19: 0x00007ffff6d859d7 libgobject-2.0.so.0`g_signal_emit_valist + 55
frame #20: 0x00007ffff6d85a94 libgobject-2.0.so.0`g_signal_emit + 148
frame #21: 0x00007ffff78337c0 libgtk-4.so.1`___lldb_unnamed_symbol18445 + 1552
frame #22: 0x00007ffff6e0f43e libglib-2.0.so.0`___lldb_unnamed_symbol2332 + 30
frame #23: 0x00007ffff6e0e199 libglib-2.0.so.0`___lldb_unnamed_symbol2326 + 249
frame #24: 0x00007ffff6e6d3bf libglib-2.0.so.0`___lldb_unnamed_symbol2631 + 831
frame #25: 0x00007ffff6e0d712 libglib-2.0.so.0`g_main_context_iteration + 50
frame #26: 0x00007ffff6fd7ed6 libgio-2.0.so.0`g_application_run + 374
frame #27: 0x000055555555731f Aqueduct`main(argc=1, argv=0x00007fffffffe5f8) at main.c:212:16
frame #28: 0x00007ffff6ac1cd0 libc.so.6`___lldb_unnamed_symbol3264 + 128
frame #29: 0x00007ffff6ac1d8a libc.so.6`__libc_start_main + 138
frame #30: 0x00005555555565b5 Aqueduct`_start + 37
Here is the code including the signal handler login_callback
:
#include "AqueductConfig.h"
#include "message.h"
#include "util.h"
#include <curl/curl.h>
#include <curl/easy.h>
#include <gtk/gtk.h>
#include <gtk/gtksingleselection.h>
#include <json-c/json.h>
#include <json_tokener.h>
#include <string.h>
static void on_message_setup(GtkSignalListItemFactory *self, GtkListItem *list_item) {
GtkWidget *label = gtk_label_new("");
gtk_list_item_set_child(list_item, label);
gtk_widget_set_halign(label, GTK_ALIGN_START);
}
static void on_message_bind(GtkSignalListItemFactory *factory, GtkListItem *list_item) {
GtkWidget *label = gtk_list_item_get_child(list_item);
AqueductMessage *message = gtk_list_item_get_item(list_item);
gtk_label_set_text(GTK_LABEL(label), aqueduct_message_get_content(message));
}
static void render_message_list(GtkWidget *window) {
// box to wrap the chat and the text box
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_widget_set_margin_start(box, 10);
gtk_widget_set_margin_end(box, 10);
gtk_widget_set_margin_top(box, 5);
gtk_widget_set_margin_bottom(box, 5);
GtkListItemFactory *message_factory = gtk_signal_list_item_factory_new();
g_signal_connect(message_factory, "setup", G_CALLBACK(on_message_setup), NULL);
g_signal_connect(message_factory, "bind", G_CALLBACK(on_message_bind), NULL);
GListStore *message_list_model = g_list_store_new(AQUEDUCT_TYPE_MESSAGE);
// select messages
GtkSingleSelection *message_list_selection_model =
gtk_single_selection_new(G_LIST_MODEL(message_list_model));
GtkWidget *message_list_view =
gtk_list_view_new(GTK_SELECTION_MODEL(message_list_selection_model), message_factory);
// pin to bottom
gtk_widget_set_vexpand(message_list_view, TRUE);
gtk_widget_set_valign(message_list_view, GTK_ALIGN_END);
gtk_box_append(GTK_BOX(box), message_list_view);
GtkWidget *text_view_scr_window = gtk_scrolled_window_new();
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(text_view_scr_window), TRUE);
gtk_scrolled_window_set_max_content_height(GTK_SCROLLED_WINDOW(text_view_scr_window), 200);
GtkWidget *text_view = gtk_text_view_new();
// `gtk_text_view` margin sets the inner padding of the element
gtk_text_view_set_top_margin(GTK_TEXT_VIEW(text_view), 5);
gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(text_view), 5);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view), 10);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_view), 10);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_CHAR);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(text_view_scr_window), text_view);
gtk_box_append(GTK_BOX(box), text_view_scr_window);
gtk_window_set_child(GTK_WINDOW(window), box);
}
typedef struct {
GtkEntryBuffer *email_buffer;
GtkEntryBuffer *password_buffer;
GtkWidget *error_label;
GtkWidget *window;
} LoginCallbackData;
char *session_token = NULL;
static void login_callback(GtkWidget *button, LoginCallbackData *data) {
const char *email = gtk_entry_buffer_get_text(data->email_buffer);
const char *password = gtk_entry_buffer_get_text(data->password_buffer);
unsigned long email_len = strlen(email);
unsigned long password_len = strlen(password);
if (email_len == 0 || password_len == 0) {
gtk_label_set_text(GTK_LABEL(data->error_label), "All fields must be defined.");
return;
}
if (email_len > 254 || email_len + password_len > 900) {
return;
}
char *data_json = g_malloc0(1024);
snprintf(data_json, 1024,
"{\"email\":\"%s\",\"password\":\"%s\",\"friendly_name\":\"Aqueduct Desktop App\"}",
email, password);
struct curl_slist *headers = curl_slist_append(NULL, "Accept: application/json");
struct curl_writefunction_memory *memory;
curl_writefunction_memory_init(memory);
CURL *curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://127.0.0.1:8000/auth/session/login");
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data_json);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, curl_writefunction_cb);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)memory);
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
CURLcode result = curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
curl_slist_free_all(headers);
curl_handle = NULL;
if (result != CURLE_OK) {
g_print("curl error: %i\n", result);
return;
}
json_object *root_json_object = json_tokener_parse(memory->response);
curl_writefunction_memory_free(memory);
json_object *error_json_object = json_object_object_get(root_json_object, "error");
if (error_json_object != NULL) {
const char *error = json_object_get_string(error_json_object);
gtk_label_set_text(GTK_LABEL(data->error_label), error);
json_object_put(error_json_object);
return;
}
json_object *token_json_object = json_object_object_get(root_json_object, "token");
if (token_json_object == NULL || !json_object_is_type(token_json_object, json_type_string)) {
gtk_label_set_text(GTK_LABEL(data->error_label),
"Could not receive session token. Check logs for more info.");
const char *root_str = json_object_to_json_string(root_json_object);
g_print("returned json: %s\n", root_str);
return;
}
g_signal_handlers_disconnect_by_func(button, login_callback, data);
session_token = g_malloc(65);
strcpy(session_token, json_object_get_string(token_json_object));
json_object_put(token_json_object);
json_object_put(root_json_object);
render_message_list(data->window);
}
static void render_login_page(GtkWidget *window) {
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
// center horizontally and vertically
gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
GtkWidget *title_label = gtk_label_new("Aqueduct");
GtkWidget *email_entry = gtk_entry_new();
gtk_entry_set_placeholder_text(GTK_ENTRY(email_entry), "Email");
GtkWidget *password_entry = gtk_entry_new();
gtk_entry_set_placeholder_text(GTK_ENTRY(password_entry), "Password");
gtk_entry_set_visibility(GTK_ENTRY(password_entry), FALSE);
GtkWidget *error_label = gtk_label_new(NULL);
GtkWidget *login_button = gtk_button_new_with_label("Login");
LoginCallbackData *data = g_malloc(sizeof(LoginCallbackData));
data->email_buffer = gtk_entry_get_buffer(GTK_ENTRY(email_entry));
data->password_buffer = gtk_entry_get_buffer(GTK_ENTRY(password_entry));
data->error_label = error_label;
data->window = window;
g_signal_connect(login_button, "clicked", G_CALLBACK(login_callback), data);
gtk_box_append(GTK_BOX(box), title_label);
gtk_box_append(GTK_BOX(box), email_entry);
gtk_box_append(GTK_BOX(box), password_entry);
gtk_box_append(GTK_BOX(box), error_label);
gtk_box_append(GTK_BOX(box), login_button);
gtk_window_set_child(GTK_WINDOW(window), box);
}
static void on_activate(GtkApplication *app) {
GtkWidget *window = gtk_application_window_new(app);
gtk_window_set_default_size(GTK_WINDOW(window), AQUEDUCT_WINDOW_DEFAULT_WIDTH,
AQUEDUCT_WINDOW_DEFAULT_HEIGHT);
gtk_window_set_title(GTK_WINDOW(window), AQUEDUCT_WINDOW_TITLE);
render_login_page(window);
gtk_window_present(GTK_WINDOW(window));
}
int main(int argc, char **argv) {
curl_global_init(CURL_GLOBAL_DEFAULT);
GtkApplication *app = gtk_application_new(AQUEDUCT_APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
curl_global_cleanup();
return status;
}
Here is also the code for curl_writefunction_cb
:
#include "util.h"
#include <stdlib.h>
#include <string.h>
void curl_writefunction_memory_free(struct curl_writefunction_memory *memory) {
free(memory->response);
}
void curl_writefunction_memory_init(struct curl_writefunction_memory *memory) {
memory->response = malloc(1);
memory->size = 0;
}
size_t curl_writefunction_cb(char *data, size_t size, size_t nmemb, void *clientp) {
size_t realsize = size * nmemb;
struct curl_writefunction_memory *memory = (struct curl_writefunction_memory *)clientp;
char *ptr = realloc(memory->response, memory->size + realsize + 1);
if (!ptr) {
return 0; // no memory
}
memory->response = ptr;
memcpy(memory->response + memory->size, data, realsize);
memory->size += realsize;
memory->response[memory->size] = '\0';
return realsize;
}
This was compiled on an x86_64 machine running Arch Linux.
Thank you in advance for your help.