Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Static global variable access checking for Lua 5.4 based on bytecode parsing

NotificationsYou must be signed in to change notification settings

muhmuhten/bcstrict

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 

Repository files navigation

Runtime static global checker for Lua 5.4.

This sounds a bit like a oxymoron, but to be specific:

  • bcstrict checks for accesses to unexpected globals within a chunkwithout executing it, by inspecting its bytecode.

  • bcstrict is intended to be executed by running Lua code on itself, at startup time, without explicit user(/author) intervention.

If called early, this lookskind of like perl'suse strict 'vars'. More so than strict.lua, in any case. If you squint.

Usage

-- check this filerequire "bcstrict"()-- allow access via _G and nothing elserequire "bcstrict"{_G=1}-- no direct global access at allrequire "bcstrict"{}local _G = _ENV--[[ .. do things ... ]]-- opportunistic checkingdo        local ok, strict = pcall(require, "bcstrict")        if ok then strict() endend-- check some other chunklocal bcstrict = require "bcstrict"local chunk = assert(loadfile "other.lua")bcstrict(_ENV, chunk)-- prevent usage anywhere elsepackage.loaded.bcstrict = function () end

Compatibility

The technique used by bcstrict is generally applicable; it was not hard to port from 5.3 to 5.4, and I don't expect it to be tremendously difficult to port to future versions of Lua, though it does hard-code some bytecode and dump format guts. Earlier versions would additionally require a replacementtable.pack, which is used to infer bytecode-relevant platform details (endianness, integer encodes wihch would otherwise also have to be hard-coded.

As far as I know, the representation of precompiled chunks is guaranteed not to change within a Lua version (x.y, e.g. 5.4) and always breaks between versions. This version of bcstrict is written and tested with Lua 5.4.3, so itshould be compatible with all 5.4.z releases.

No effort goes into making this run on any Lua version other than the one whose bytecode it parses, but Lua 5.4 issyntactically backward-compatible enough with almost all 5.y code to at leastcompile it well enough to run bcstrict as a static analyzer.

There is also an older version of this code which targets 5.3, which can be found in the branches.

Limitations

You must call the function returned byrequire "bcstrict"! Since require avoids loading a module more than once, but there may be multiple files which need to be checked, each user of bcstrict has to actually run it.

Due to the design constraint of being implemented by parsing dumped bytecode, bcstrict has a slightly interesting concept of a global access: a get or set to a field of an upvalue which is, or can be traced up to, the first (and only!) upvalue of a chunk is forbidden if they key used does not exist the environment provided (or _ENV) when bcstrict is called.

It doesn't track any other variables. In particular, itwon't catch "globals" that access a declared local _ENV, and itwill complain even when you access fields of _ENV explicitly, e.g.:

-- OKrequire "bcstrict"()local _ENV = _ENVprint(not_defined)-- not OKrequire "bcstrict"()print(_ENV["not_defined"])

In addition, bcstrict does nothing useful when called on non-chunk functions. Because global access compiles as an upvalue table access, it is fundamentally impossible to figure out which, if any, of a non-chunk function's upvalues is the top-level _ENV. For example, the inner functions returned by the following snippets compile to identical bytecode, but close over different variables.

local afunction f(b)        return function ()                a.c = d + b.e        endendlocal afunction g(b)        return function ()                c = a.d + b.e        endendlocal a, bfunction h(_ENV)        return function ()                a.c = b.d + e        endend

Debug informationcould be used to identify _ENV, if available; however, as the last example shows, it will also flag intentionally redefined local _ENV.

why this

Lua's default behavior of silently accepting access to undefined (misspelled, out-of-scope, &c.) variables is hilariously error-prone and literally my #1 source of bugs while writing this damn module. There are three or so well-known ways of combatting this issue:

  • Careful testing. Look, if it works for you...

  • Set a metatable on the global environment table. Often good enough, but has side-effects which may make it unsuitable for libraries. More critically, however, this approach won't catch errors that only occur on code paths thatdidn't occur during testing.

  • Some sort of static analyzer. Probablyluacheck. This works pretty well ... but you have to run it as a separate step.

This is an attempt to capture the benefits of static analysis (at least in the scope of preventing undeclared variable accesses) with minimal user overhead.

About

Static global variable access checking for Lua 5.4 based on bytecode parsing

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp