Skip to content

SEGFAULT in lv_opengles_window_delete() #9214

@davidljung

Description

@davidljung

LVGL version

v9.3.0-556-gc016f72d4

Platform

Platform: Ubuntu Linux 22.04 / GLFW + OpenGL ES backend
Compiler: GCC 11.4

What happened?

After calling lv_opengles_window_delete() on the only open window, a SEGFAULT is caused due to stack-overflow (infinite recursion). Hence, it isn't possible to open a window, close it and open another. The code appears to be trying to cleanup and exit as it was the last window, but the code is buggy and would have called exit(0) anyway, which isn't desirable. I have #define LV_LOG_LEVEL LV_LOG_LEVEL_INFO in lv_config.h which might impact it, since if logging it off the recursion may not occur (but the exit(0) still would).

I had Claude.ai create a small reproduction .cpp and explain what I found using gdb.
Thank you.

-- AI text below (take care):

Calling lv_opengles_window_delete() on the last window causes the program to SEGFAULT, making it impossible to programmatically close windows without terminating the entire application.

Expected Behavior

When closing a window programmatically via lv_opengles_window_delete(), the window should close and the program should continue running normally. The application should be able to:

  1. Close all windows
  2. Continue executing code
  3. Potentially create new windows later
  4. Clean up and exit gracefully when ready

Actual Behavior

When lv_opengles_window_delete() is called on the last window:

  1. The function detects the window list is empty (lv_opengles_glfw.c:189)
  2. It calls lv_glfw_window_quit() (lv_opengles_glfw.c:190)
  3. lv_glfw_window_quit() calls glfwTerminate() (line 441) and sets glfw_inited = false (line 442)
  4. Then it calls lv_deinit() (line 444)
  5. During lv_deinit(), the logging system tries to get a timestamp via lv_tick_get()
  6. lv_tick_get() calls lv_glfw_tick_count_callback() which calls glfwGetTime()
  7. Since GLFW was already terminated in step 3, this triggers a GLFW error
  8. The GLFW error callback glfw_error_cb() tries to log the error using LV_LOG_ERROR()
  9. This triggers another call to lv_tick_get(), creating infinite recursion
  10. The stack overflows, causing a SEGFAULT
  11. The program never reaches exit(0) at line 446

Root Cause

There are two related problems in src/drivers/opengles/lv_opengles_glfw.c:

Problem 1: Automatic shutdown when last window closes

// Line 189-190 in lv_opengles_window_delete()
if(lv_ll_is_empty(&glfw_window_ll)) {
    lv_glfw_window_quit();  // Automatically called!
    ...
}

Problem 2: Incorrect shutdown order causing infinite recursion

// Line 436-446
static void lv_glfw_window_quit(void)
{
    lv_timer_delete(update_handler_timer);
    update_handler_timer = NULL;

    glfwTerminate();      // Line 441: Terminates GLFW
    glfw_inited = false;  // Line 442: Marks GLFW as not initialized

    lv_deinit();          // Line 444: Tries to deinit LVGL
                          //          But during deinit, logging calls lv_tick_get()
                          //          which calls glfwGetTime() on terminated GLFW
                          //          triggering error callback that tries to log
                          //          causing infinite recursion and stack overflow!

    exit(0);              // Line 446: Never reached due to SEGFAULT above
}

The proper order should be:

  1. Delete the LVGL timer first ✓
  2. Call lv_deinit() BEFORE terminating GLFW (so logging still works)
  3. Then call glfwTerminate()
  4. Optionally call exit(0) or let the application decide

How to reproduce?

/*
 * LVGL Window Close Bug Demonstration
 *
 * This program demonstrates that calling lv_opengles_window_delete() on the last
 * window causes the program to SEGFAULT, making it impossible to programmatically
 * close windows without terminating the entire application.
 *
 * Expected behavior: Window should close and program should continue running.
 * Actual behavior: Program crashes/hangs in an infinite loop when the last window
 *                 is deleted, because lv_opengles_window_delete() calls
 *                 lv_glfw_window_quit() which SEGFAULTs before attempting to call exit(0).
 *
 * Root cause: In lv_opengles_glfw.c:
 *   - Line 189-190: When window list becomes empty, calls lv_glfw_window_quit()
 *   - Line 436-446: lv_glfw_window_quit() calls glfwTerminate(), lv_deinit(),
 *                   and exit(0)
 *
 * Build manually:
 *   g++ -std=c++17 lvgl_window_close_bug.cpp \
 *       -I../lvgl \
 *       -Lbuild/lvgl/lib \
 *       -llvgl -lglfw -lGL -lGLEW -lfreetype -lpng -lpthread \
 *       -o lvgl_window_close_bug
 */

#include <stdio.h>
#include <unistd.h>
#include <GLFW/glfw3.h>
#include <lvgl.h>

int main() {
    printf("=== LVGL Window Close Bug Demonstration ===\n\n");

    // Initialize GLFW
    printf("1. Initializing GLFW...\n");
    if (!glfwInit()) {
        fprintf(stderr, "Failed to initialize GLFW\n");
        return 1;
    }
    printf("   GLFW initialized successfully\n\n");

    // Set up OpenGL ES context hints
    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

    // Initialize LVGL
    printf("2. Initializing LVGL...\n");
    lv_init();
    printf("   LVGL initialized successfully\n\n");

    // Create LVGL window
    printf("3. Creating LVGL window (640x480)...\n");
    lv_opengles_window_t* lv_window = lv_opengles_glfw_window_create(640, 480, true);
    if (!lv_window) {
        fprintf(stderr, "Failed to create LVGL window\n");
        return 1;
    }
    printf("   LVGL window created at %p\n\n", (void*)lv_window);

    // Create display texture
    printf("4. Creating display texture...\n");
    lv_display_t* lv_display = lv_opengles_texture_create(640, 480);
    if (!lv_display) {
        fprintf(stderr, "Failed to create display texture\n");
        lv_opengles_window_delete(lv_window);
        return 1;
    }
    printf("   Display texture created at %p\n\n", (void*)lv_display);

    // Add texture to window
    printf("5. Adding texture to window...\n");
    int32_t texture_id = lv_opengles_texture_get_texture_id(lv_display);
    lv_opengles_window_texture_t* window_texture = lv_opengles_window_add_texture(
        lv_window, texture_id, 640, 480
    );
    if (!window_texture) {
        fprintf(stderr, "Failed to add texture to window\n");
        lv_display_delete(lv_display);
        lv_opengles_window_delete(lv_window);
        return 1;
    }
    printf("   Window texture created at %p\n", (void*)window_texture);
    printf("   Texture ID: %d\n\n", texture_id);

    // Wait a bit to show the window
    printf("6. Window created and displayed. Waiting 2 seconds...\n");
    sleep(2);
    printf("   Done waiting\n\n");

    // Now close the window programmatically
    printf("7. Attempting to close window programmatically...\n");
    printf("   Step 7a: Removing window texture...\n");
    lv_opengles_window_texture_remove(window_texture);
    printf("   Step 7a: ✓ Texture removed successfully\n\n");

    printf("   Step 7b: Deleting display...\n");
    lv_display_delete(lv_display);
    printf("   Step 7b: ✓ Display deleted successfully\n\n");

    printf("   Step 7c: Deleting LVGL window...\n");
    printf("   WARNING: This will SEGFAULT if it's the last window!\n");
    printf("   About to call lv_opengles_window_delete()...\n");
    fflush(stdout);

    lv_opengles_window_delete(lv_window);

    // This line will NEVER be reached because lv_opengles_window_delete() causes infinite recursion
    //  (I think it calls lvgl_deinit and then tries to use log causing an error it tried to log)
    printf("   Step 7c: ✓ Window deleted successfully\n\n");

    printf("8. Cleaning up...\n");
    printf("   This code should be reachable, but it won't be SEGFAULT\n");
    glfwTerminate();

    printf("\n=== Test completed successfully ===\n");
    printf("If you see this message, the bug has been fixed!\n");

    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions