718 karma · joined October 4, 2015
What's also true, and what I think the GP was trying to get at, is that STUN requires an external coordination server. UPnP (I think—I am far less familiar with it) does not, because in UPnP you're negotiating the holepunching with the local router directly, whereas STUN is sort of using a loophole.
Not that it's necessarily malicious in intent—requiring “extra verification” only on suspected misuse isn't entirely unreasonable, and that's awfully hard to distinguish from things like targeted privacy attacks—but it would be nice to establish this sort of thing as common knowledge, especially since I imagine the chaff of “huh? no they don't” is only to their benefit.
Alternatively, if you're only concerned about other privileged ports, with a systemd socket unit, you could have systemd do the binding and pass your unprivileged process the socket, so that your process no longer needs the privileged-bind capability at any time; that'd probably be the easiest way if you're willing to modify the application code a bit. If you don't like systemd, you could have another helper program do it.
Or you could put your process in its own network namespace which has a filtered bridging or routing configuration between it and whichever namespace has the ‘real’ connectivity, which is complex and awkward but allows your process to ‘think’ it has full access, which might help if the code would otherwise balk.
Or you could use various other userspace sandboxing frameworks that let you install a BPF syscall filter, which I like a lot in theory but don't currently have personal experience with.
This reminds me of a private virtual event I put on a few years ago which was done entirely over real-time text. There were a few people who wanted to be there, but wound up having a time conflict, so I set up to make a recording of the event—including timestamps, and providing a way to play it back correctly timed (optionally modulated by a speed control). It seemed to get a good reception.
I don't think it uses any of the techniques currently considered central to machine learning, but if it works well / catches on to start with then it could be a good place to see how useful those would be.
: foo ( something ) ;
: bar 2dup foo 1+ * ; \ or whatever
\ ... in something loaded later on ...
: foo ( other thing entirely ) ;
: xyzzy foo bar ; \ calls later foo, then earlier bar which calls earlier foo
… which means it's harder to run into problems with stepping on “internal” symbols even if you're using short names in the “same” dictionary. “Purely” interpreted Forth does happen at the top level, in which lookups happen just before execution, but the part of it that isn't being used for compilation tends to be small fragments to kick off execution or to enter commands interactively. (In colorForth, phase distinctions are made by color instead, but I believe it also does early-bound compilation.)In (a usual) Lisp (here I'm centering on Common Lisp and Scheme), function and variable lookups default to late binding, but based on symbol objects rather than strings. The symbols themselves contain pointers to function objects and/or variable contents, so there's a level of indirection, but it's not a hash table lookup every time. (Indeed in CL you can have “uninterned” symbols which usually have a name but can't be looked up by that name.) Again the mapping from strings, that is, packages in CL, obarrays in Emacs Lisp, etc. generally remains available at runtime, so you can do (find-symbol "FOO"), but when processing definitions, the lookup from string to symbol happens at read time. Then in CL there's some restrictions on redefinitions (http://www.lispworks.com/documentation/HyperSpec/Body/03_bbc...) that make it easier for compilers to do more localized early binding even between top-level definitions.
Traditional BASICs I vaguely recall being closer to “fully” interpreted than this, but I haven't studied those enough to say—and I wouldn't expect that to apply to later BASIC revivals after a certain point.
An interesting other example would be Lua, which uses late binding semantics for global and method lookups, and then (here I'm thinking of the mainline PUC implementation) makes them faster by interning all strings at the C level, in a way which isn't exposed to the Lua level. When all strings with the same contents have the same pointer, string equality is very cheap, which makes table lookups over the string objects cheap. But lexical locals are compiled into index lookups, which are even cheaper, and it's often considered good practice to write internal file-level definitions as locals, import external module tables as locals, etc. so in practice once again some of the late binding disappears along the way.
interface SomeProcessor<E> {
void process(Thing thing) throws E;
}
<E> void processThingsSomehow(
SomeProcessor<E> processor,
Container<Thing> things,
Parameter how) throws E {
// ... {
processor.process(extractedThing);
// } ...
}
and then later: void processOrFail(Thing thing)
throws SomeCheckedException {
// ...
}
void processOurThings() {
try {
someObject.processThingsSomehow(
this::processOrFail,
getThings(), HOW);
} catch (SomeCheckedException e) {
// ...
}
}
and it definitely worked the way I expected—if the correct exceptions weren't caught in processOurThings, it wouldn't compile, and processThingsSomehow did not have to catch them. It even worked in at least some cases with multiple throws on the concrete SomeProcessor, though I think the different exceptions involved had an upper type bound within the package; I don't know how well that's handled in the general case.There's a post elsewhere on “The Amish, and Strategic Norms around Technology” which has some not-bad short description of ways this isn't universal, and potential for application to current-day (potentially America-centric, etc.) society, without going into too much depth: https://www.lesswrong.com/posts/36Dhz325MZNq3Cs6B/the-amish-...
That said, this is something I did with a cheap laptop for a while—installed a basic Linux and Emacs and used it as a mostly-isolated Emacsing machine; I believe I never hooked it up to the network again, though I did sketch out some provisions for manually physically disconnecting sensitive filesystems in case I needed to do that very occasionally (while being aware that sufficiently clever malware could still work around this). But then the logic board malfunctioned much sooner than I'd expected, and I haven't been in a position to get another since.
And 100% air-gapping is not always desirable. Many 1990s-and-before machines that had networking capabilities were still the epitome of “local first” by comparison to the “deployment is primarily done through progressive Web apps” world of 2021. So you could have a few channels by which to talk to the outside world, but without the outside world getting its hooks deep inside you in the process: the television without the telescreen. (Security issues aside, of course—though some of that is mitigable with restrictive proxying, simpler protocols, etc. depending on how retro and how networked you are trying for.)
Past me, however, did things like write an OTP generator for TI-83+ partly on the grounds that it was a non-networked device and simple enough to leave less in the way of interesting ways for malware to hide.
And if you try to combine namespaces with DTDs (which is just an explosive mix to start with, and I think is just recommended to never do) you get other problems, because you're no longer allowed to add arbitrary namespace declarations in the middle, so anything that round-trips prefixes but might ever add redundant declarations of them won't reliably produce something that DTD-validates, and if you're transforming into a DTD from something that might have used other namespaces, you have to make sure to remove all the extra declarations, and…
Note that most of this is still “well-defined”, it's just awkwardly hairy. This is not to be taken as an excuse to implement the standard badly or incorrectly if you're going to handle it at all.
(I originally wrote this comment with a more fully worked-out example, but after viewing it in context I realized it was way too long to be an only-partly-on-topic comment on this thread, so I'll probably move it to a post elsewhere and submit it later.)
I've done something similar for weakly-secured spendable tokens in the past, packing some orderly lookup state into 64 bits and then exposing the Speck64/32. That application was actually backed by an object-array sort of storage with numeric indices, so I stored something like object ID, timestamp, and sequence number, and then looked up the right property on the object at validation time. (If I needed strongly-in-the-crypto-sense unguessable IDs for security, I wouldn't do this without doing more research/analysis, since a database-wide key feels like it could be a weak point; compare how peppering is used for password storage in conjunction with salting but not as a replacement. Someone else probably already knows that answer.)
(Edit to add cross-reference: ah, looks like tmpyt in another comment claims that YouTube used to do this but then switched away because of the “knowing the small key lets you enumerate everything” loophole.)
I also don't know of any other HLL JITs that use TCG, which seems like weak evidence that it wouldn't be a win.
The BEAM is currently a bytecode interpreter. The article describes it as using threaded code, which if you look at the C source is true in a sense on platforms where the C compiler provides the GNU-style extension of being able to take the address of labels for dynamic `goto` (https://stenmans.org/happi_blog/?p=194 agrees with my skim of `beam_emu.c`)—though if that's the only use as it seems, I might find that an edge case of the term, since I associate “threaded code” with the style used in Forth where “user-level” subroutines can be pointed to directly. If computed `goto` isn't available, then it uses a conventional switch/case loop, repeatedly dispatching the result of fetching the next bytecode instruction pointer value.