[client] overlay: fix unsafe ImGui string handling

This commit is contained in:
Geoffrey McRae
2026-07-30 09:08:25 +10:00
parent 0738ea402a
commit 9c24acc6f9
5 changed files with 86 additions and 30 deletions

View File

@@ -28,10 +28,11 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/appstrings.h" #include "common/appstrings.h"
#include "common/stringutils.h"
typedef struct ConfigCallback typedef struct ConfigCallback
{ {
const char * title; char * title;
void * udata; void * udata;
void (*callback)(void * udata, int * id); void (*callback)(void * udata, int * id);
} }
@@ -61,7 +62,10 @@ static void config_freeList(struct ll * list)
{ {
ConfigCallback * cb; ConfigCallback * cb;
while(ll_shift(list, (void **)&cb)) while(ll_shift(list, (void **)&cb))
{
free(cb->title);
free(cb); free(cb);
}
ll_free(list); ll_free(list);
} }
@@ -78,9 +82,9 @@ static void config_renderLGTab(void)
if (igCollapsingHeader_BoolPtr("Donations", NULL, if (igCollapsingHeader_BoolPtr("Donations", NULL,
ImGuiTreeNodeFlags_DefaultOpen)) ImGuiTreeNodeFlags_DefaultOpen))
{ {
igTextWrapped(LG_DONATION_STR); igTextWrapped("%s", LG_DONATION_STR);
igBeginTable("split", 2, 0, (ImVec2){}, 0.0f); igBeginTable("donations_split", 2, 0, (ImVec2){}, 0.0f);
igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, fontSize, 0); igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, fontSize, 0);
igTableNextColumn(); igTableNextColumn();
igBulletText(""); igBulletText("");
@@ -93,12 +97,12 @@ static void config_renderLGTab(void)
if (igCollapsingHeader_BoolPtr("Help & Support", NULL, if (igCollapsingHeader_BoolPtr("Help & Support", NULL,
ImGuiTreeNodeFlags_DefaultOpen)) ImGuiTreeNodeFlags_DefaultOpen))
{ {
igBeginTable("split", 2, 0, (ImVec2){}, 0.0f); igBeginTable("help_support_split", 2, 0, (ImVec2){}, 0.0f);
igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, fontSize * 9.0f, 0); igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, fontSize * 9.0f, 0);
for(const StringPair * help = LG_HELP_LINKS; help->name; ++help) for(const StringPair * help = LG_HELP_LINKS; help->name; ++help)
{ {
igTableNextColumn(); igTableNextColumn();
igBulletText(help->name); igBulletText("%s", help->name);
igTableNextColumn(); igTableNextColumn();
overlayTextMaybeURL(help->value, true); overlayTextMaybeURL(help->value, true);
} }
@@ -113,7 +117,7 @@ static void config_renderLGTab(void)
if (igTreeNode_Str(member->name)) if (igTreeNode_Str(member->name))
{ {
igSpacing(); igSpacing();
igTextWrapped(member->blurb); igTextWrapped("%s", member->blurb);
if (member->donate[0].name) if (member->donate[0].name)
{ {
igSeparator(); igSeparator();
@@ -122,13 +126,13 @@ static void config_renderLGTab(void)
"do so directly via the following platform%s:", "do so directly via the following platform%s:",
member->donate[1].name ? "s" : ""); member->donate[1].name ? "s" : "");
igBeginTable("split", 2, 0, (ImVec2){}, 0.0f); igBeginTable("member_donations_split", 2, 0, (ImVec2){}, 0.0f);
igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed,
fontSize * 10.0f, 0); fontSize * 10.0f, 0);
for(const StringPair * donate = member->donate; donate->name; ++donate) for(const StringPair * donate = member->donate; donate->name; ++donate)
{ {
igTableNextColumn(); igTableNextColumn();
igBulletText(donate->name); igBulletText("%s", donate->name);
igTableNextColumn(); igTableNextColumn();
overlayTextMaybeURL(donate->value, false); overlayTextMaybeURL(donate->value, false);
} }
@@ -143,11 +147,11 @@ static void config_renderLGTab(void)
static void config_renderLicenseTab(void) static void config_renderLicenseTab(void)
{ {
igText(LG_COPYRIGHT_STR); igTextUnformatted(LG_COPYRIGHT_STR, NULL);
overlayTextURL(LG_WEBSITE_URL, NULL); overlayTextURL(LG_WEBSITE_URL, NULL);
igText(LG_VERSION_STR); igTextUnformatted(LG_VERSION_STR, NULL);
igSeparator(); igSeparator();
igTextWrapped(LG_LICENSE_STR); igTextWrapped("%s", LG_LICENSE_STR);
} }
static int config_render(void * udata, bool interactive, struct Rect * windowRects, static int config_render(void * udata, bool interactive, struct Rect * windowRects,
@@ -246,6 +250,12 @@ struct LG_OverlayOps LGOverlayConfig =
static void config_addToList(struct ll * list, const char * title, static void config_addToList(struct ll * list, const char * title,
void(*callback)(void * udata, int * id), void * udata) void(*callback)(void * udata, int * id), void * udata)
{ {
if (!title || !*title)
{
DEBUG_ERROR("configuration title must not be empty");
return;
}
ConfigCallback * cb = calloc(1, sizeof(*cb)); ConfigCallback * cb = calloc(1, sizeof(*cb));
if (!cb) if (!cb)
{ {
@@ -253,7 +263,14 @@ static void config_addToList(struct ll * list, const char * title,
return; return;
} }
cb->title = title; cb->title = lg_strdup(title);
if (!cb->title)
{
DEBUG_ERROR("failed to allocate ram");
free(cb);
return;
}
cb->udata = udata; cb->udata = udata;
cb->callback = callback; cb->callback = callback;
ll_push(list, cb); ll_push(list, cb);

View File

@@ -24,6 +24,7 @@
#include "../main.h" #include "../main.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/stringutils.h"
#include "overlay_utils.h" #include "overlay_utils.h"
struct GraphState struct GraphState
@@ -36,7 +37,7 @@ static struct GraphState gs = {0};
struct OverlayGraph struct OverlayGraph
{ {
const char * name; char * name;
RingBuffer buffer; RingBuffer buffer;
bool enabled; bool enabled;
float min; float min;
@@ -50,7 +51,7 @@ static void configCallback(void * udata, int * id)
igCheckbox("Show timing graphs", &gs.show); igCheckbox("Show timing graphs", &gs.show);
igSeparator(); igSeparator();
igBeginTable("split", 2, 0, (ImVec2){}, 0); igBeginTable("graphs_split", 2, 0, (ImVec2){}, 0);
GraphHandle graph; GraphHandle graph;
ll_lock(gs.graphs); ll_lock(gs.graphs);
@@ -89,7 +90,10 @@ static void graphs_free(void * udata)
{ {
struct OverlayGraph * graph; struct OverlayGraph * graph;
while(ll_shift(gs.graphs, (void **)&graph)) while(ll_shift(gs.graphs, (void **)&graph))
{
free(graph->name);
free(graph); free(graph);
}
ll_free(gs.graphs); ll_free(gs.graphs);
gs.graphs = NULL; gs.graphs = NULL;
} }
@@ -231,6 +235,12 @@ struct LG_OverlayOps LGOverlayGraphs =
GraphHandle overlayGraph_register(const char * name, RingBuffer buffer, GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
float min, float max, GraphFormatFn formatFn) float min, float max, GraphFormatFn formatFn)
{ {
if (!name || !*name)
{
DEBUG_ERROR("graph name must not be empty");
return NULL;
}
struct OverlayGraph * graph = malloc(sizeof(*graph)); struct OverlayGraph * graph = malloc(sizeof(*graph));
if (!graph) if (!graph)
{ {
@@ -238,7 +248,14 @@ GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
return NULL; return NULL;
} }
graph->name = name; graph->name = lg_strdup(name);
if (!graph->name)
{
DEBUG_ERROR("out of memory");
free(graph);
return NULL;
}
graph->buffer = buffer; graph->buffer = buffer;
graph->enabled = true; graph->enabled = true;
graph->min = min; graph->min = min;
@@ -250,10 +267,11 @@ GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
void overlayGraph_unregister(GraphHandle handle) void overlayGraph_unregister(GraphHandle handle)
{ {
if (!gs.graphs) if (!gs.graphs || !handle)
return; return;
ll_removeData(gs.graphs, handle); ll_removeData(gs.graphs, handle);
free(handle->name);
free(handle); free(handle);
if (gs.show) if (gs.show)

View File

@@ -75,7 +75,7 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects
else else
igText("%s+%c", escapeName, handle->charcode); igText("%s+%c", escapeName, handle->charcode);
igTableNextColumn(); igTableNextColumn();
igText(handle->description); igTextUnformatted(handle->description, NULL);
} }
igEndTable(); igEndTable();

View File

@@ -205,35 +205,56 @@ MsgBoxHandle overlayMsg_show(
const char * caption, MsgBoxConfirmCallback confirm, void * opaque, const char * caption, MsgBoxConfirmCallback confirm, void * opaque,
const char * fmt, va_list args) const char * fmt, va_list args)
{ {
struct Msg * msg = malloc(sizeof(*msg)); struct Msg * msg = calloc(1, sizeof(*msg));
if (!msg) if (!msg)
{ {
DEBUG_ERROR("out of memory"); DEBUG_ERROR("out of memory");
return NULL; return NULL;
} }
msg->caption = strdup(caption); msg->caption = lg_strdup(caption && *caption ? caption : "Message");
if (!msg->caption)
{
DEBUG_ERROR("out of memory");
free(msg);
return NULL;
}
msg->lines = stringlist_new(false); msg->lines = stringlist_new(false);
if (!msg->lines)
{
free(msg->caption);
free(msg);
return NULL;
}
msg->confirm = confirm; msg->confirm = confirm;
msg->opaque = opaque; msg->opaque = opaque;
valloc_sprintf(&msg->message, fmt, args); if (valloc_sprintf(&msg->message, fmt ? fmt : "", args) < 0)
{
DEBUG_ERROR("failed to format message");
freeMsg(msg);
return NULL;
}
char * token = msg->message; char * token = msg->message;
char * rest = msg->message; for(char * rest = msg->message; ; ++rest)
do
{ {
if (*rest == '\n') if (*rest == '\n')
{ {
*rest = '\0'; *rest = '\0';
stringlist_push(msg->lines, token); stringlist_push(msg->lines, token);
token = rest + 1; token = rest + 1;
continue;
} }
++rest;
}
while(*rest != '\0');
if (*token) if (*rest == '\0')
stringlist_push(msg->lines, token); {
if (*token)
stringlist_push(msg->lines, token);
break;
}
}
ll_push(l_msg.messages, msg); ll_push(l_msg.messages, msg);
app_invalidateOverlay(false); app_invalidateOverlay(false);

View File

@@ -69,7 +69,7 @@ static void overlayAddUnderline(ImU32 color)
void overlayTextURL(const char * url, const char * text) void overlayTextURL(const char * url, const char * text)
{ {
igText(text ? text : url); igTextUnformatted(text ? text : url, NULL);
if (igIsItemHovered(ImGuiHoveredFlags_None)) if (igIsItemHovered(ImGuiHoveredFlags_None))
{ {
@@ -87,9 +87,9 @@ void overlayTextMaybeURL(const char * text, bool wrapped)
if (strncmp(text, "https://", 8) == 0) if (strncmp(text, "https://", 8) == 0)
overlayTextURL(text, NULL); overlayTextURL(text, NULL);
else if (wrapped) else if (wrapped)
igTextWrapped(text); igTextWrapped("%s", text);
else else
igText(text); igTextUnformatted(text, NULL);
} }
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image, bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,