SIGSEGV after button clicked signal handling

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.

Apparently it was an issue relating to struct curl_writefunction_memory not actually being allocated and json_object_put(token_json_object) when token_json_object is not owned by the caller. Closing now.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.