Tags: luau-lang/lute
Tags
refactor: split `cliruntimefixture` into .h and .cpp (#426) continuation of #416 (comment) to avoid duplicating a `static` function each time the header is included --------- Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Fix use-after-free bug in `process.cpp` (#421) ### Problem The issue can be replicated with a simple script that calls `process.run` twice, like this: ```luau local process = require("@lute/process") process.run({"echo", "hello!"}) process.run({"echo", "goodbye!"}) ``` Very rarely, some runs throw unexpected errors, crash, or corrupt Lute output with random bytes. The problem is a subtle UAF bug in `process.cpp`. Despite the small PR diff, this was a very tricky bug to nail down. Had to go digging pretty deep into the libuv source to understand what was going on. Essentially, it's not correct to check if the `stdout` and `stderr` pipes are active before closing them. Since these pipes are used as read-only data streams, they're automatically made inactive by libuv upon reaching EOF. In this situation, we don't need to call `uv_read_stop` on these streams because they're already stopped (although it is fine to do it; `uv_read_stop` is [idempotent](https://docs.libuv.org/en/v1.x/stream.html#c.uv_read_stop)), but we do still need to close the underlying pipe handle. We're currently in a situation where calling `process.run` might immediately close either the `stdout` or `stderr` streams (at EOF), causing the `uv_is_active` check to fail, preventing us from calling `uv_close` on the handle and from incrementing `pendingCloses`. Failing to close these handles means that libuv retains data internally associated with them, and by not incrementing `pendingCloses` (essentially a refcount), we end up destroying the owning `ProcessHandle` of these handles before libuv has been able to clean them up internally. Then, when we go to initialize new handles for the next `process.run` call, we have a UAF manifest deep inside of libuv's circular doubly linked queue code: adding a new handle to the data structure requires adjusting pointers on the last-added handle, which happens to be a handle created from the previous `process.run` call. At this point, it is freed memory that was supposed to be removed from libuv's internals before being freed (using `uv_close`). ### Solution Check instead if the handles still need closing with `!uv_is_closing` since `uv_is_active` is not relevant to whether `uv_close` needs to be called in this case.
stdlib: Introduce a small standard library utility for writing tests … …in lute (#393) Based on [frktest](https://github.com/itsfrank/frktest) This testing utility needs a bunch of assertions to be more useful, but this PR just scopes out what a simple, builtin testing api in Luau could look like. For some tests that look like: ``` local test = require("@std/test") local check = test.assert test.case("foo", function() check.eq(3, 3) check.eq(6, 1) end) test.case("bar", function() check.eq("a", "b") end) test.case("baz", function() check.neq("a", "b") end) test.suite("MySuite", function() test.case("subcase", function() check.eq(5 * 5, 3) end) end) test.run(); ``` this will render as: ``` ❯ ./build/xcode/debug/lute/cli/lute run examples/testing.luau ================================================== TEST RESULTS ================================================== Failed Tests (3): ❌ foo ./examples/testing.luau:5 eq: 6 ~= 1 ❌ bar ./examples/testing.luau:10 eq: a ~= b ❌ MySuite.subcase ./examples/testing.luau:20 eq: 25 ~= 3 -------------------------------------------------- Total: 4 Passed: 1 ✓ Failed: 3 ✗ ================================================== ``` Currently reports: - [x] Test suites and cases - [x] Line numbers - [x] Stack traces - [x] The expression that failed.
Update the bootstrap script to actually build two copies of lute (#386) The bootstrap script didn't correctly generate the lute standard library headers that the `luthier` script did. This PR: - generates a minimal header and implementation for the lute standard libraries during the bootstrap phase - uses these headers to build a `bootstrapped-lute` binary - uses that `bootstrapped-lute` to run `luthier.luau` which builds the actual `lute` executable with the `std` library correctly generated. - accepts an `--install` argument to install this `lute` binary to `$HOME/.lute/bin`. There are some additional improvements that have been made: 1. General README fixes 2. This script can now be run multiple times in sequence, and it correctly deletes artifacts generated by the bootstrapping process each time (e.g. generated std headers, dependencies). 3. Uses git commands to `clone` only the particular revision of the `extern` dependencies we care about, not the entire history. --------- Co-authored-by: ariel <aweiss@hey.com>
Manually get the full body including any \0 instead of relying on the… … string length (#379) Without this we would get a truncated body and curl would hang waiting for the missing bytes to send. Test: ```luau local net = require("@lute/net") local pp = require("@batteries/pp") local function test(payload: string) local response = net.request("https://httpbin.org/post", { method = "POST", headers = { ["Content-Type"] = "application/octet-stream", }, body = payload, }) print(response.status, pp(response.headers), response.ok, pp(response.body)) end test("Hello World") -- works test("Hello\0World") -- hangs ``` `Content-Length` is 11 for both.
Implement task.delay (#372) This PR implements task.delay in the form of `<T..., U...>(dur: number | time.Duration, routine: (T...) -> U..., ...: T...)`. `yieldLuaStateFor` was also changed to accept an `nargs` parameter, to support using that function for `task.delay`. Closes #260 --------- Co-authored-by: ariel <aweiss@hey.com>
PreviousNext