-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
impose configurable hard limit on message sizes #4280
Comments
This ticket needs to be split between Swingset and cosmic-swingset. @warner please help come up with a plan. |
@warner This needs an estimate. |
@mhofman does this need to get done in the Cosmic Swingset layer? In Go or JS? |
We probably should do it as early as possible, so in Go |
Probably something to discuss at the new kernel meeting. |
At today's kernel meeting, we figured:
Also:
There is a lot more depth to this sort of limiting (and some sort of economic metering is the long-term defense: maybe you can give zoe large arguments but you have to pay for it, and zoe uses those funds to pay for the resources it needs to hold them internally). But the part that we care about for MN-1 is protecting Zoe and other infrastructure vats from external attack. The liveslots/netstring limits put a sharp edge behind these vats, and attackers can push those vats up against that edge. So our action items are:
|
Oh yeah if XS reports the length in codepoints then a it can technically go 4x instead of at worse going 3x between |
We decided that the existing cosmos message size limit might be sufficient. We need to figure out the expansion factor from that end of the stack to Zoe, and make sure we are safe for MN-1. If so, we can move this ticket to MN-1.1. |
Note: the netstring code can accept a limit, but we don't yet set one, pending a decision about what the limit ought to be. |
What is the Problem Being Solved?
One defense against #4276 is to limit message sizes. This depends upon #3269 to remove the important use case for large contract-installation messages, but once that's done, we feel comfortable rejecting messages larger than, say, 1MB in length. Our general expectation is that messages should be shorter than 10kB, since the chain is not the place to be processing large amounts of data, but that will probably change over time.
There are at least half a dozen places where a message size limit might be imposed:
syscall.send
s from the mailbox device and puts them on the run-queuesyscall.send
syscall.send
There are small formatting differences at the boundaries between the different layers. For example, when vat-comms receives
$seqnum:$ackSeqNum:deliver:${remoteTargetSlot}:${method}:${remoteResultSlot}:${slots..};${args.body}
, it parses out everything, maps targets and slots through the per-remote c-list, then performs asyscall.send
with a lot of the same arguments (including a verbatim copy ofargs.body
, which requires doing aJSON.stringify
of a structure with those pieces. We can count the number of copies ofargs.body
to estimate the short-term memory needs, and we can estimate the size of all the other parts to get a sense of how large thesyscall.send
will be as a function of the incomingdispatch.deliver
arguments. Because of this overhead, a 1_000_000 byte hard limit on the Mailbox-side message will still occasionally allow a 1_000_005 byte message atsyscall.send
. If we think the variation in overhead can't be exploited, then we could rely upon the Mailbox limit and not have the kernel impose an additional one, or e.g. have the kernel impose a 2MB limit internally.All limits need to be configurable by the host application, so our chain can use a governance process to raise them later.
Description of the Design
I'm thinking that the host application (which decides when to write inbound messages into the Mailbox device) could impose the limit, in which case we might not need anything else.
If we choose to have the kernel impose a message limit on all vats, then we should add a config knob like
sendMessageBodyLimit
, with the highest allowablemsg.body
size that will be accepted on anysyscall.send
,syscall.resolve
, orsyscall.invoke
. And acontroller.setSendMessageBodyLimit()
to update it later. The config default should be 1_000_000.Security Considerations
We should implement this now, without solving the full problem (the perfect is the enemy of the good), but we should also think through the myriad ways to sidestep or trick this limit into doing something surprising. In other protocols, having validation or limits applied at multiple points is a great bug farm, because e.g. if the difference in parsing between the first and second checks means only a small set of messages get caught by the second check, it will be under-tested. If the limit really is critical, like if the second step will die if it ever gets 1_000_001 bytes, then the check must be repeated at each step, as long as there is any possibility of variation.
We should also be clear what we think this is protecting. By setting the limit at N, we're claiming confidence that we can survive anything
<= N
. We are not claiming that we know of an attack which would be enabled byN + 1
, and we're not claiming to be happy about imposing such a small limit. From a "providing features" point of view, we'd like to have as high a limit as possible, or no limit at all. From a "defending against unexpected attacks" pov, we'd make the limit as small as we can get away with.Our general plan is to impose fees that rise (possibly super-linearly). If these are painfully expensive by the time they reach the hard limits, that will provide a clear incentive to do smaller things, or break them up into smaller pieces, both of which I think match our "do big things off-chain" story.
Test Plan
Unit tests at each of the component boundaries to show both
= N
passing and= N+1
failing cases.The text was updated successfully, but these errors were encountered: