From f2fb71cd8919bb3cdfe52d748c76b3a5bbb72726 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 14 Aug 2026 22:10:40 +0900 Subject: [PATCH] fix(ibus): make the X11 frontend fail truthfully --- ibus.c | 97 ++++++++++++++++++++++++++++++++++-------- tests/ibus_live_test.c | 24 ++++++++++- tests/ibus_test.c | 22 +++++++++- 3 files changed, 121 insertions(+), 22 deletions(-) diff --git a/ibus.c b/ibus.c index e111f6f..e7de213 100644 --- a/ibus.c +++ b/ibus.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,9 +13,9 @@ enum { - Maxconns = 16, + Maxconns = Maxclients, Maxwatches = 2*Maxconns + 1, - Maxcontexts = 64, + Maxcontexts = Maxclients, Relmask = 1<<30, Ibussupermask = 1<<26, /* IBus wire constants; the daemon does not link libibus. */ @@ -136,11 +137,16 @@ xdisplay(char *host, int hsz, char *num, int nsz) static int buildaddrpath(char *buf, int sz) { - char mid[64], host[64], num[8], *cfg, *home; + char mid[64], host[64], num[8], *cfg, *home, *explicit; struct stat st; char dir[512]; int n; + explicit = getenv("IBUS_ADDRESS_FILE"); + if(explicit != nil){ + n = snprintf(buf, sz, "%s", explicit); + return explicit[0] == '\0' || n < 0 || n >= sz ? -1 : 0; + } machineid(mid, sizeof(mid)); if(mid[0] == '\0') return -1; @@ -311,6 +317,12 @@ newcontext(DBusConnection *conn, const char *path) return nil; } +static int +clientpreedit(Ictx *ctx) +{ + return (ctx->cap & Ibuscappreedit) != 0; +} + static void sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res) { @@ -318,7 +330,7 @@ sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res) memset(&kr, 0, sizeof kr); kr.owner = ctx; - kr.cap = ctx->cap & Ibuscappreedit ? Cclientpreedit : 0; + kr.cap = clientpreedit(ctx) ? Cclientpreedit : 0; kr.op = op; kr.ks = ks; kr.mod = mod; @@ -487,6 +499,9 @@ handlehello(DBusConnection *c, DBusMessage *m) char name[32]; const char *np; + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "Hello expects no arguments"); busctr++; snprintf(name, sizeof(name), ":1.%d", busctr); np = name; @@ -561,6 +576,9 @@ handlecreate(DBusConnection *c, DBusMessage *m) Ictx *ctx; int n; + if(!dbus_message_has_signature(m, "s")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "CreateInputContext expects one client name"); icctr++; n = snprintf(path, sizeof(path), "/org/freedesktop/IBus/InputContext_%d", icctr); @@ -594,7 +612,8 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx) DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)){ dbus_error_free(&err); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "ProcessKeyEvent expects three unsigned integers"); } USED(code); if(!processkey(ctx, sym, state, &res)) @@ -603,7 +622,7 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx) stoutf(&res.preedit, preedit, sizeof preedit); if(commit[0] != '\0') emitcommit(c, dbus_message_get_path(m), commit); - if(ctx->cap & Ibuscappreedit) + if(clientpreedit(ctx)) emitpreedit(ctx, preedit); return handlebool(c, m, res.eaten); } @@ -625,7 +644,8 @@ handlereset(DBusConnection *c, DBusMessage *m, Ictx *ctx) Keyres res; sendrequest(ctx, Keyreset, 0, 0, &res); - emitpreedit(ctx, ""); + if(clientpreedit(ctx)) + emitpreedit(ctx, ""); return handlenoop(c, m); } @@ -640,7 +660,8 @@ static DBusHandlerResult handlefocusout(DBusConnection *c, DBusMessage *m, Ictx *ctx) { releasecontext(ctx); - emitpreedit(ctx, ""); + if(clientpreedit(ctx)) + emitpreedit(ctx, ""); return handlenoop(c, m); } @@ -740,7 +761,8 @@ handlepropertyset(DBusConnection *c, DBusMessage *m, Ictx *ctx) ctx->hints = hints; if(ctx->focused && !washidden && hidden(ctx)){ sendrequest(ctx, Keyreset, 0, 0, &res); - emitpreedit(ctx, ""); + if(clientpreedit(ctx)) + emitpreedit(ctx, ""); } return handlenoop(c, m); } @@ -896,6 +918,9 @@ handleintrospect(DBusConnection *c, DBusMessage *m) DBusMessage *r; const char *xml = introspectxml; + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "Introspect expects no arguments"); r = dbus_message_new_method_return(m); dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID); dbus_connection_send(c, r, nil); @@ -926,8 +951,12 @@ onmsg(DBusConnection *c, DBusMessage *m, void *_) if(strcmp(member, "GetNameOwner") == 0) return handlenameowner(c, m); if(strcmp(member, "AddMatch") == 0 - || strcmp(member, "RemoveMatch") == 0) + || strcmp(member, "RemoveMatch") == 0){ + if(!dbus_message_has_signature(m, "s")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "match rule must be one string"); return handlenoop(c, m); + } } if(strcmp(iface, "org.freedesktop.IBus") == 0){ if(strcmp(member, "CreateInputContext") == 0) @@ -952,20 +981,40 @@ onmsg(DBusConnection *c, DBusMessage *m, void *_) "unknown input context"); if(strcmp(member, "ProcessKeyEvent") == 0) return handlekey(c, m, ctx); - if(strcmp(member, "FocusIn") == 0) + if(strcmp(member, "FocusIn") == 0){ + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "FocusIn expects no arguments"); return handlefocusin(c, m, ctx); - if(strcmp(member, "FocusOut") == 0) + } + if(strcmp(member, "FocusOut") == 0){ + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "FocusOut expects no arguments"); return handlefocusout(c, m, ctx); - if(strcmp(member, "Reset") == 0) + } + if(strcmp(member, "Reset") == 0){ + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "Reset expects no arguments"); return handlereset(c, m, ctx); - if(strcmp(member, "Destroy") == 0) + } + if(strcmp(member, "Destroy") == 0){ + if(!dbus_message_has_signature(m, "")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "Destroy expects no arguments"); return handledestroy(c, m, ctx); + } if(strcmp(member, "SetCursorLocation") == 0) return handlecursor(c, m, ctx); if(strcmp(member, "SetCapabilities") == 0) return handlecap(c, m, ctx); - if(strcmp(member, "SetEngine") == 0) + if(strcmp(member, "SetEngine") == 0){ + if(!dbus_message_has_signature(m, "s")) + return handleerror(c, m, DBUS_ERROR_INVALID_ARGS, + "SetEngine expects one engine name"); return handlenoop(c, m); + } } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -977,11 +1026,16 @@ newconn(DBusServer *s, DBusConnection *c, void *_) USED(s); USED(_); - if(nconns >= Maxconns) + if(nconns >= Maxconns){ + fprintf(stderr, "strans: ibus: rejecting client: %d-connection limit reached\n", + Maxconns); + dbus_connection_close(c); return; + } if(!dbus_connection_set_watch_functions(c, addwatch, removewatch, togglewatch, nil, nil)){ fprintf(stderr, "strans: ibus: cannot watch client connection\n"); + dbus_connection_close(c); return; } memset(&vt, 0, sizeof(vt)); @@ -989,6 +1043,7 @@ newconn(DBusServer *s, DBusConnection *c, void *_) if(!dbus_connection_register_fallback(c, "/", &vt, nil)){ fprintf(stderr, "strans: ibus: cannot register client handler\n"); dbus_connection_set_watch_functions(c, nil, nil, nil, nil, nil); + dbus_connection_close(c); return; } dbus_connection_ref(c); @@ -1085,9 +1140,13 @@ ibusthread(void *_) USED(_); threadsetname("ibus"); if(ibusinit() < 0) - return; + die("ibus: initialization failed"); replyc = chancreate(sizeof(Keyres), 0); + if(replyc == nil) + die("ibus: cannot create reply channel"); for(;;){ + if(!dbus_server_get_is_connected(srv)) + die("ibus: server disconnected"); n = 0; for(i = 0; i < nwatches && n < Maxwatches; i++){ if(watches[i] == nil) @@ -1104,8 +1163,10 @@ ibusthread(void *_) n++; } rv = poll(pfds, n, 200); - if(rv < 0) + if(rv < 0 && errno == EINTR) continue; + if(rv < 0) + die("ibus: poll: %s", strerror(errno)); for(i = 0; i < n; i++){ if(pfds[i].revents == 0) continue; diff --git a/tests/ibus_live_test.c b/tests/ibus_live_test.c index 895e6e4..fc50e31 100644 --- a/tests/ibus_live_test.c +++ b/tests/ibus_live_test.c @@ -20,7 +20,7 @@ enum { - Maxconnections = 16, + Maxconnections = 64, Calltimeout = 4000, Starttimeout = 8000, Stoptimeout = 3000, @@ -312,7 +312,8 @@ startdaemon(Daemon *d, char *program, char *mapdir) setenv("XDG_CONFIG_HOME", d->config, 1) < 0 || setenv("HOME", d->home, 1) < 0 || unsetenv("DISPLAY") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || - unsetenv("IBUS_ADDRESS") < 0){ + unsetenv("IBUS_ADDRESS") < 0 || + unsetenv("IBUS_ADDRESS_FILE") < 0){ dprintf(STDERR_FILENO, "set daemon environment: %s\n", strerror(errno)); _exit(126); @@ -1346,6 +1347,9 @@ runlifecycle(Daemon *d) goto out; if(!createcontext(c1, a, sizeof a) || !createcontext(c2, b, sizeof b)) goto out; + if(!unknowncall(c1, a, "ProcessKeyEvent", DBUS_ERROR_INVALID_ARGS) || + !unknowncall(c1, a, "SetEngine", DBUS_ERROR_INVALID_ARGS)) + goto out; if(!unknowncall(c1, "/org/freedesktop/IBus/InputContext_missing", "FocusIn", DBUS_ERROR_UNKNOWN_OBJECT)) goto out; @@ -1477,6 +1481,22 @@ runpolicy(Daemon *d) if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, NULL, "zero-capability language") || !expectkey(c1, &s1, a, 'k', 0, 1, NULL, "zero-capability key") || + !emptycall(c1, &s1, a, "Reset") || + !nosignal(&s1, "zero-capability Reset") || + !expectkey(c1, &s1, a, 'k', 0, 1, NULL, + "zero-capability before PASSWORD") || + !contentcall(c1, &s1, a, Purposepassword, 0) || + !nosignal(&s1, "zero-capability PASSWORD") || + !contentcall(c1, &s1, a, Purposefree, 0) || + !nosignal(&s1, "zero-capability leave PASSWORD") || + !expectkey(c1, &s1, a, 'k', 0, 1, NULL, + "zero-capability before FocusOut") || + !emptycall(c1, &s1, a, "FocusOut") || + !nosignal(&s1, "zero-capability FocusOut") || + !emptycall(c1, &s1, a, "FocusIn") || + !nosignal(&s1, "zero-capability refocus") || + !expectkey(c1, &s1, a, 'k', 0, 1, NULL, + "zero-capability after refocus") || !capcall(c1, &s1, a, 1<<1) || !nosignal(&s1, "non-preedit cap") || !capcall(c1, &s1, a, Cappreedit|(1<<1)) || !preedit(&s1, a, "k", 1, 1, "enable inline") || diff --git a/tests/ibus_test.c b/tests/ibus_test.c index bf49d5e..2e91fbb 100644 --- a/tests/ibus_test.c +++ b/tests/ibus_test.c @@ -30,8 +30,8 @@ void ibus_machine_id_fallback(struct ct *t) { char root[] = "/tmp/strans-machine-id.XXXXXX"; - char primary[128], fallback[128], got[64]; - char *path[2]; + char primary[128], fallback[128], override[128], got[512]; + char *path[2], *old, *saved; FILE *fp; int madeprimary, madefallback; @@ -61,6 +61,24 @@ ibus_machine_id_fallback(struct ct *t) path[1] = fallback; machineidfiles(got, sizeof got, path, nelem(path)); CT_EQ_STR(t, "fallback-machine-id", got); + + old = getenv("IBUS_ADDRESS_FILE"); + saved = old == nil ? nil : strdup(old); + if(old == nil || CT_CHECK(t, saved != nil)){ + if(CT_CHECK(t, snprintf(override, sizeof override, "%s/address", root) + < (int)sizeof override) && + CT_CHECK(t, setenv("IBUS_ADDRESS_FILE", override, 1) == 0)){ + CT_EQ_INT(t, 0, buildaddrpath(got, sizeof got)); + CT_EQ_STR(t, override, got); + CT_CHECK(t, setenv("IBUS_ADDRESS_FILE", "", 1) == 0); + CT_EQ_INT(t, -1, buildaddrpath(got, sizeof got)); + } + if(saved != nil){ + setenv("IBUS_ADDRESS_FILE", saved, 1); + free(saved); + }else + unsetenv("IBUS_ADDRESS_FILE"); + } cleanup: if(madefallback) CT_CHECK(t, unlink(fallback) == 0);