Skip to content

Add lua_eval command, lua IPC command, and scroll.context_container API#347

Open
jbms wants to merge 5 commits into
dawsers:masterfrom
jbms:lua-eval-and-ipc
Open

Add lua_eval command, lua IPC command, and scroll.context_container API#347
jbms wants to merge 5 commits into
dawsers:masterfrom
jbms:lua-eval-and-ipc

Conversation

@jbms

@jbms jbms commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

lua_eval allows inline lua to be evaluated without needing to write it to a file.

Compared to just running lua or lua_eval via RUN_COMMAND, the new LUA_EXEC command allows a return value to be sent back to the client.

The scroll.context_container() API provides access to the context container when lua or lua_eval is run with criteria.

@jbms jbms changed the title Add lua_eval command and lua IPC command Add lua_eval command, lua IPC command, and scroll.context_container API Jun 30, 2026
@jbms jbms force-pushed the lua-eval-and-ipc branch 3 times, most recently from 5850d90 to 1761821 Compare July 1, 2026 18:44
jbms added 5 commits July 1, 2026 14:38
During workspace switch animations, references to the source and target
workspaces (data->from and data->to) are tracked. If a workspace is destroyed
while the animation is active (e.g., when it is emptied or its output is
unplugged), these references become dangling. Upon completion or cancellation of
the animation, accessing the workspaces triggers a use-after-free crash.

To address this, we avoid starting the switch animation if either the
source or target workspace is already marked for destruction (which is
when they can be freed).

Here is a minimal shell script to reproduce the crash on a build without this fix:

```bash
#!/bin/bash
set -x

# Setup isolated run environment
TEMP_DIR=$(mktemp -d)
export XDG_RUNTIME_DIR="$TEMP_DIR"
export WLR_BACKENDS=headless
export WLR_LIBINPUT_NO_DEVICES=1
export LSAN_OPTIONS=detect_leaks=0
unset DISPLAY
unset WAYLAND_DISPLAY

# Create minimal configuration
cat <<EOF > "$TEMP_DIR/config"
animations enabled on
EOF

# Start scroll compositor headlessly
./build/sway/scroll -c "$TEMP_DIR/config" -d > "$TEMP_DIR/scroll.log" 2>&1 &
SCROLL_PID=$!

# Wait for the compositor to start up and open its wayland socket
for i in {1..50}; do
    SOCKET_PATH=$(find "$XDG_RUNTIME_DIR" -name "wayland-*" | head -n 1)
    if [ -n "$SOCKET_PATH" ] && [ -S "$SOCKET_PATH" ]; then
        break
    fi
    sleep 0.1
done

if [ -z "$SOCKET_PATH" ] || [ ! -S "$SOCKET_PATH" ]; then
    echo "Error: scroll failed to start or open socket"
    cat "$TEMP_DIR/scroll.log"
    kill $SCROLL_PID || true
    rm -rf "$TEMP_DIR"
    exit 1
fi

SOCKET_NAME=$(basename "$SOCKET_PATH")

# Switch to workspace 2 and immediately switch back to workspace 1 to trigger UAF
export SWAYSOCK="$XDG_RUNTIME_DIR/i3-ipc.sock"
if [ ! -S "$SWAYSOCK" ]; then
    # Look for the IPC socket
    SWAYSOCK=$(find "$XDG_RUNTIME_DIR" -name "*.sock" | head -n 1)
fi

echo "Using SWAYSOCK=$SWAYSOCK"
./build/swaymsg/scrollmsg -s "$SWAYSOCK" "workspace 2; workspace 1" 2>&1

# Wait a brief moment for the crash to complete and the process to terminate
sleep 0.5

# Check if the compositor crashed
if kill -0 $SCROLL_PID 2>/dev/null; then
    echo "Compositor is still running! Reproduction FAILED."
    echo "=== COMPOSITOR LOG ==="
    cat "$TEMP_DIR/scroll.log"
    kill $SCROLL_PID
    rm -rf "$TEMP_DIR"
    exit 1
else
    echo "Compositor has terminated (expected)!"
    cat "$TEMP_DIR/scroll.log"
    rm -rf "$TEMP_DIR"
    exit 0
fi
```
Exposes scroll.animating() and scroll.pending_transactions() to Lua
to allow external scripts and tests to query whether the compositor
has settled.

Also includes testing infrastructure updates (pytest.ini config, LSan
suppressions, and helper methods in test_utils.py).
Add a new `lua_eval` compositor command to allow executing a raw inline
Lua string as code rather than loading a file. Any optional arguments
passed are supplied to the inline Lua block as parameters (accessed
via `...`). The empty string is used as the script identifier.

Also update the `scroll` manual page and README.md to document the
new command.
Add a new `lua` IPC command type (represented by `IPC_LUA_EXEC` = 124)
to support evaluating either a Lua script file or inline Lua code.
The message accepts arguments as a list of JSON values which are parsed
and converted to Lua values, and serializes the return value(s) of the
script back to JSON before replying.

In the `scrollmsg` client, expose this through separate `lua` (for
running a file) and `lua_eval` (for running inline code) command
types.

Also:
- Export and reuse `sway_lua_value_to_json` and
  `sway_lua_table_to_json` from `sway/lua.c` for Lua-to-JSON
  serialization in the IPC reply.
- Export `sway_lua_push_json_to_lua` for JSON-to-Lua argument
  conversion.
- Fix relative stack index handling in JSON/Lua conversion functions to
  ensure they function correctly with negative stack indices.
- Commit dirty transactions immediately after the script execution on
  both success and error execution paths.
- Rewrite the `execute_lua` test helper to use the new IPC API directly
  (removing temporary files and the custom Lua runner).
- Update man pages (scrollmsg.1, scroll-ipc.7) and README.md.
Exposes the criteria-matched container context to running Lua scripts
via a new `scroll.context_container()` function.

If a script is run via criteria (e.g., `[class="XTerm"] lua_eval ...`),
the function returns the matched container's ID. If executed globally
without criteria, it returns `nil`.

To prevent nested executions of `scroll.command()` within a script
from permanently overwriting the context, the active Lua context is
backed up on entry to Lua command handlers (`lua` and `lua_eval`) and
restored on exit.

Also documents the new API in `scroll.lua` and the `scroll.5` man
page, and adds comprehensive pytest coverage.
@jbms jbms force-pushed the lua-eval-and-ipc branch from 1761821 to 4c03191 Compare July 1, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant