Bug 146793 - [GTK] Web processes should not have unlimited access to memory
Summary: [GTK] Web processes should not have unlimited access to memory
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKitGTK (show other bugs)
Version: 528+ (Nightly build)
Hardware: PC Linux
: P2 Normal
Assignee: Michael Catanzaro
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-07-09 10:35 PDT by Michael Catanzaro
Modified: 2017-03-11 11:00 PST (History)
11 users (show)

See Also:


Attachments
Patch (3.41 KB, patch)
2015-07-09 10:44 PDT, Michael Catanzaro
no flags Details | Formatted Diff | Diff
Patch (3.57 KB, patch)
2015-07-10 14:46 PDT, Michael Catanzaro
no flags Details | Formatted Diff | Diff
Patch (4.76 KB, patch)
2015-07-28 08:15 PDT, Michael Catanzaro
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Catanzaro 2015-07-09 10:35:49 PDT
Web processes should not have unlimited access to memory.

A quick example of why not: open a bunch of bugs on Red Hat Bugzilla in new tabs. [1] If you open ~20 bugs, at least one (usually about three for me) should run out of control, allocating memory until your computer begins to swap excessively. Mine hangs for an hour, forcing me to power off. This happens to me multiple times per day. (Curious if any folks from Apple can reproduce this in Safari.) Even though it's a bug that should be fixed, other similar bugs exist, like [2] and [3], or might exist in the future, so we should be robust to this by setting a memory limit for the web process.

On Linux the options for limiting memory are RLIMIT_AS and RLIMIT_DATA. RLIMIT_RSS exists but doesn't do anything; even if it worked, it wouldn't be useful for the problem I want to solve, which is swapping. RLIMIT_DATA is not useful in practice since it doesn't affect mmap(), which is what we use to allocate memory, and also what malloc() uses. So RLIMIT_AS is the only option. I've picked a 5 GB address space limit, which should work for a long time in the future. In practice, this limits web processes to ~1.4 GB of memory on my machine. A well-behaved web process uses 50-100 MiB (although I've seen them go as high as ~250 MiB after a sufficiently long time loading many pages and leaking much memory), so this is so wildly higher than necessary that it shouldn't hurt normal operation. (Of course, we might need to increase it in the distant future.)

[1] https://bugzilla.redhat.com/buglist.cgi?component=epiphany&product=Fedora
[2] https://bugs.webkit.org/show_bug.cgi?id=139847
[3] https://bugs.webkit.org/show_bug.cgi?id=126122
Comment 1 Michael Catanzaro 2015-07-09 10:44:15 PDT
Created attachment 256489 [details]
Patch
Comment 2 Martin Robinson 2015-07-09 10:47:07 PDT
Comment on attachment 256489 [details]
Patch

What happens when a WebProcess hits this limit? Does it crash?
Comment 3 Michael Catanzaro 2015-07-09 10:50:51 PDT
(In reply to comment #2)
> Comment on attachment 256489 [details]
> Patch
> 
> What happens when a WebProcess hits this limit? Does it crash?

It causes mmap() to return ENOMEM. If that happens to WTF::OSAllocator, it will immediately call CRASH(). Other call sites could handle it differently, of course. In practice, with my reproducer, the web process loads for some time (30-45 seconds) and then crashes.
Comment 4 Carlos Alberto Lopez Perez 2015-07-09 11:07:13 PDT
Comment on attachment 256489 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=256489&action=review

> Source/WebKit2/Shared/unix/ChildProcessMain.cpp:62
> +    // The address space limit is currently set to 5 GB. In practice, this causes a runaway web
> +    // process to cap out at about 1.4 GB of allocated memory.
> +    static const auto addressSpaceLimitBytes = 5000000000l;
> +    rlimit64 rlim = {addressSpaceLimitBytes, addressSpaceLimitBytes};
> +    rlim.rlim_max = rlim.rlim_cur;
> +    if (setrlimit64(RLIMIT_AS, &rlim))

I think that we should allow at runtime to tune this limit as also to disable any limit check. Maybe with an environment variable?
Comment 5 Michael Catanzaro 2015-07-09 11:31:11 PDT
Good idea. An environment variable sounds best. Do you prefer something short like WEBKIT_CHILD_RLIMIT_AS or WEBKIT_CHILD_ASLIMIT, or something more descriptive like WEBKIT_CHILD_ADDRESS_SPACE_LIMIT? If set to 0 the limit would be disabled, otherwise it would be applied in bytes.
Comment 6 Carlos Garcia Campos 2015-07-09 23:08:46 PDT
Comment on attachment 256489 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=256489&action=review

> Source/WebKit2/Shared/unix/ChildProcessMain.cpp:46
> +void ChildProcessMainBase::setProcessResourceLimits()

Only resource this limits is memory, no? I would call it setMemoryLimit, or even setHeap Limit, don't need to a use Process in the method name either, since it's a method of a ChildProcess class.

> Source/WebKit2/Shared/unix/ChildProcessMain.h:40
> +    virtual void setProcessResourceLimits();

Why is this virtual?
Comment 7 Michael Catanzaro 2015-07-10 05:39:40 PDT
Do you think we should provide a way to override the limit at compile time as well? I imagine the address space required could vary considerably depending on the distribution. Something like:

#ifndef WEBKIT_CHILD_ADDRESS_SPACE_LIMIT
#define WEBKIT_CHILD_ADDRESS_SPACE_LIMIT 5000000000l
#endif

(In reply to comment #6)
> Only resource this limits is memory, no? I would call it setMemoryLimit, or
> even setHeap Limit, don't need to a use Process in the method name either,
> since it's a method of a ChildProcess class.

Let's call it setAddressSpaceLimit. I wish something as simple as a "memory" limit was possible in Linux.

(In reply to comment #6) 
> > Source/WebKit2/Shared/unix/ChildProcessMain.h:40
> > +    virtual void setProcessResourceLimits();
> 
> Why is this virtual?

Since all the other member functions of ChildProcessMain are virtual, to allow child processes to have different resource limits. We could leave it non-virtual until a child process wants to do this; I don't care.
Comment 8 Carlos Alberto Lopez Perez 2015-07-10 05:49:22 PDT
(In reply to comment #5)
> Good idea. An environment variable sounds best. Do you prefer something
> short like WEBKIT_CHILD_RLIMIT_AS or WEBKIT_CHILD_ASLIMIT, or something more
> descriptive like WEBKIT_CHILD_ADDRESS_SPACE_LIMIT? If set to 0 the limit
> would be disabled, otherwise it would be applied in bytes.

I like more the first one, but also the others are fine.
Comment 9 Michael Catanzaro 2015-07-10 14:46:21 PDT
Created attachment 256616 [details]
Patch
Comment 10 Jérémy Lal 2015-07-13 10:04:48 PDT
Hi !
i built this map using javascript, dom, svg and external http resources:
http://assets-carte.lefigaro.fr/cartes/common/france-resultats-2014.jpg
it was a ~4GB memory usage and the only engine that didn't crash or hang or error out or leak like crazy was webkitgtk...
While the rationale is certainly good, this limit sounds a bit like shooting oneself in the foot, it should be documented upfront !
Comment 11 Michael Catanzaro 2015-07-13 10:09:13 PDT
Not sure I understand the problem. My web processes require < 50 MiB to display that image.
Comment 12 Jérémy Lal 2015-07-13 10:11:09 PDT
(In reply to comment #11)
> Not sure I understand the problem. My web processes require < 50 MiB to
> display that image.

The image is a jpg created from a big svg file created by those means.
Comment 13 Michael Catanzaro 2015-07-28 07:50:30 PDT
This broke Java. The plugin process needs to be exempt from the limit.
Comment 14 Michael Catanzaro 2015-07-28 08:15:21 PDT
Created attachment 257645 [details]
Patch
Comment 15 Michael Catanzaro 2015-08-23 09:12:41 PDT
Comment on attachment 257645 [details]
Patch

Perhaps predictably in retrospect, this has caused a very significant increase in OOM, and generally made the web process less reliable. I'm going to keep the patch in Fedora until the bug in comment #0 is resolved, due to the severity of that bug, but I don't recommend it this for upstream and have removed r?