Skip to content
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

Out-of-memory error in Safari (iOS/macOS) when reloading a page #19374

Closed
MathisRaibaud opened this issue May 16, 2023 · 36 comments
Closed

Out-of-memory error in Safari (iOS/macOS) when reloading a page #19374

MathisRaibaud opened this issue May 16, 2023 · 36 comments

Comments

@MathisRaibaud
Copy link

MathisRaibaud commented May 16, 2023

Hello,
I encountered an issue which seems to be systematic when building WASM code with Emscripten.

When building with -pthread enabled, the resulting WASM code make Safari crash with an error RangeError: Out-of-memory error on iOS and macOS after several reloads of the page. The number of reloads before getting the error seems to depend on the MAXIMUM_MEMORY value (or INITIAL_MEMORY value if ALLOW_MEMORY_GROWTH is disabled) and the device.

I created a simple project to reproduce the error and add my first investigations. See https://github.com/MathisRaibaud/wasm-memory-error-safari-repro

To summarize:

  • The problem seems to occur only on Safari (iOS and macOS).
  • The problem seems to occur only if the memory is shared (using -pthread flag)
  • The error seems to be related to the MAXIMUM_MEMORY value (or to the INITIAL_MEMORY value if ALLOW_MEMORY_GROWTH is disabled).
  • From what I experimented, the memory seems to be never deallocated when the page is reloaded and therefore creates an out-of-memory after several reloads.

What do you think? Is there anything I misunderstood or do you think it may come from an Emscripten or Safari bug?

Thank you so much for your help and your amazing work 🙏

EDIT: The problem seems to be related to the auto-generated Emscripten script (and not Safari), as it does not occur when shared memory is allocated manually (see #19374 (comment)).

@sbc100
Copy link
Collaborator

sbc100 commented May 16, 2023

How many threads are you trying to create? Perhaps limit you threads to just a few (2 or 4 perhaps?).

@sbc100
Copy link
Collaborator

sbc100 commented May 16, 2023

Otherwise I guess it could be a safari bug?

@stknob
Copy link

stknob commented May 16, 2023

This one maybe: https://bugs.webkit.org/show_bug.cgi?id=255103

@MathisRaibaud
Copy link
Author

@sbc100
The bug does not depend on the number of threads created. In the simple project I wrote to reproduce the bug, I don't even open a thread and yet I have the bug.

I also thought it might be a Safari bug, so I tested to run the line that crashes in the code generated by Emscripten directly in an HTML page:

  var INITIAL_MEMORY = 16777216;
  wasmMemory = new WebAssembly.Memory({
   "initial": INITIAL_MEMORY / 65536,
   "maximum": 536870912 / 65536,
   "shared": true
  });

but this time, I don't get the error after reloading the page. I only have the error when I use the code generated by Emscripten.

@stknob
Yes I saw that issue and also ffmpegwasm/ffmpeg.wasm#299 which describe a similar problem. In these two issues, users report out-of-memory error on iOS only at the first access to the page. The two issues suggested reducing the MAXIMUM_MEMORY to get rid of the out-of-memory error on iOS.

And indeed, the first access to the page works. But I found that if I reloaded the page, I still got the same error. I have the impression that the number of reloads to get the error depends on the value of MAXIMUM_MEMORY. The lower it is, the more reloads you have to do before getting the error.

I then tested on Safari macOS to identify if the problem came only from iOS. On macOS, the error also occurs, but after a larger number of reloads, probably because the memory allowed by the browser is larger on Desktop than on Mobile.

@sbc100
Copy link
Collaborator

sbc100 commented May 17, 2023

It seems this can't really be an emscripten issue.. since the whole is discarded each time you reload. I guess its good to have the discussion here so others can find it, but it doesn't seem like something we can fix on our end, right?

@MathisRaibaud
Copy link
Author

MathisRaibaud commented May 17, 2023

I understand and I was surprised myself. For me, if the page is reloaded, all resources should be deallocated. But from my experiments, it seems that this is not the case when the memory is shared. And I had this problem only with code generated by emscripten, I didn't manage to reproduce it by instantiating shared memory myself without going through emscripten, hence the fact that I opened the issue here and not in Safari/WebKit.

Just out of curiosity and if you have time, do you reproduce the bug on your side using the repo I linked in my first comment?

Thanks a lot in any case 🙂

@sbc100
Copy link
Collaborator

sbc100 commented May 17, 2023

I'm afraid I don't have a iOS or macOS device myself. But perhaps others can confirm?

@px-stkn
Copy link

px-stkn commented May 22, 2023

@sbc100 I'm getting the same "RangeError: Out of memory" after about 15 reloads on a Version 16.4 (18615.1.26.110.1) Desktop Safari (M1 MAX, 32GiB RAM), using @MathisRaibaud's repro.

And still happens with the latest macOS update applied, Safari Version 16.5 (18615.2.9.11.4), after about 30 reloads.

@ThomasHezard
Copy link

@sbc100

  • Same here, on various iOS (iphones and ipads) and macOS (Intel and ARM) devices with up-to-date OS and Safari.
  • This behaves the same for me, pointing to an Emscripten issue.

Personally not a big fan of Safari, but it is quite widely used and we have to support it 😉

It would awesome to have some update on this as it makes emscripten-generated modules quite unreliable for many users 🙏

@kripken
Copy link
Member

kripken commented May 31, 2023

As this sounds like a Safari-specific issue, it might help for more people affected by the issue to comment with details on the WebKit issue that's already been filed, that is linked here:

#19374 (comment)

More information there might help them investigate, and could also affect prioritization.

Otherwise, if someone can find a workaround we could do on the Emscripten side we'd be happy to do that, but if it's just Safari limiting the amount of memory (my best guess from the above) then I'm not sure we can do much for such a WebKit bug/limitation.

@Hiroki-Tamaru
Copy link

I am facing a similar issue. In my case, I do not set "shared": true, but the problem arises when I use WASM inside an AudioWorklet.

The "Out of memory" error occurs after repeatedly reloading a web page that simply generates a WebAssembly.Memory in an AudioWorklet, regardless of Emscripten. This issue occurs on iOS Safari and iOS Chrome. I created a bug report since I think this is a bug of WebKit.
https://bugs.webkit.org/show_bug.cgi?id=256023

Interestingly, there is a workaround that prevents the "Out of memory" error. If you just refer to the buffer of memory generated in AudioWorklet as

const mem = new WebAssembly.Memory({
  'initial': MEMORY_SIZE / 65536,
  'maximum': MEMORY_SIZE / 65536,
});
mem.buffer;

, the error goes away even though the mem.buffer line is not supposed to do anything special to the buffer.

Demo page:
https://wasm-memory-in-audioworklet-test.netlify.app/

The "Out of memory" error also occurs when instantiating WASM using Emscripten, but the code generated by Emscripten is too complex for me to analyze and I have not been able to solve the problem using the workaround successfully.

@MathisRaibaud
Copy link
Author

MathisRaibaud commented Jun 5, 2023

@Hiroki-Tamaru Thanks for your message, I came across your issue yesterday and wanted to reply, but you beat me to it! 😉

I tested your demo and had the same problem: on iOS, after ~7 reloads, I get the "Out of memory" error. And if I call mem.buffer, I no longer get the error in your demo. I also tested your demo on Safari macOS (Safari 16.4) and I also have the same problem but after a large number of reloads (~66 on my machine, it was long to test!).

In this case, Emscripten is not involved, so the problem may actually be with Safari/WebKit.

In the repository I made to reproduce the bug, I've tried your workaround, i.e refer to buffer after the init of the WebAssembly.Memory in the code generated by Emscripten:

  wasmMemory = new WebAssembly.Memory({
   "initial": INITIAL_MEMORY / 65536,
   "maximum": 536870912 / 65536,
   "shared": true
  });
  wasmMemory.buffer; // <-- added this line manually

but I still got the Out-of-memory error in my case...

@MathisRaibaud
Copy link
Author

MathisRaibaud commented Jun 5, 2023

@kripken Thank you for your reply 🙂 . The problem you reported is not really the same as the one described here. In the problem you mentioned, the insufficient memory error occurs when the page first loads, and that I can understand. On Safari iOS, there's less memory available, so if you don't reduce the MAXIMUM_MEMORY value, you'll exceed the memory limit. The problem here is a little different, we manage to load the page on Safari, but after a few reloads, the page crashes with an out-of-memory error, as if the available memory is decreasing reload after reload... which seems strange.

After your message, I've checked if there are others issues opened in WebKit that may be related to the problem described here and have found two issues:

In these two issues, an out-of-memory error is reported when reloading the page and Emscripten is not involved in either of these issues, so it might be a Safari-specific issue at the end, as you said.

@suzukieng
Copy link

suzukieng commented Jun 18, 2024

Hi everyone. This issue is biting me as it creates serious problems for the users of my barcode scanning library STRICH.
I've created a simplified reproducer repro:
https://github.com/pixelverse-llc/safari-wasm-oom-reproducer

It consists of just an index.c file containing:

#include <stdio.h>

int main() {
  printf("Hello from Safari iOS WASM OOM reproducer!\n");
  return 0;
}

and an accompanying build script:

#!/bin/sh
emcc \
    -sENVIRONMENT=web \
    -sINITIAL_MEMORY=67108864 \
    -sALLOW_MEMORY_GROWTH=1 \
    -sABORTING_MALLOC=1 \
    index.c -o index.html

Repeatedly loading the resulting index.html in Safari on iOS 17.5.1 triggers the issue after a couple of refreshes, and crucially persists across reloads, necessitating a TAB REOPEN (edit: previously though browser needed to be restarted). I will submit this reproducer to the Webkit issues mentioned in the previous post.

Edit: I can not reproduce this in the Safari iOS 18 Developer Beta (22A5282m). Can anyone confirm?

@ivancuric
Copy link

I've mitigated a related issue (OOM during growth) by forcing a larger memory allocation (500mb) on Safari, and disabling memory growth. OOM is triggered during growth, no matter the initial size, and it became worse after iOS 17.4.

In my testing, I came to the conclusion that reload OOM is triggered much earlier if using shared memory, and unlike the non-shared, which can be fixed by closing and reopening the tab, only shared memory required an entire app reload.

Can someone confirm this?

@sbc100
Copy link
Collaborator

sbc100 commented Jun 18, 2024

Hi everyone. This issue is biting me as it creates serious problems for the users of my barcode scanning library STRICH. I've created a simplified reproducer repro: https://github.com/pixelverse-llc/safari-wasm-oom-reproducer

It consists of just an index.c file containing:

#include <stdio.h>

int main() {
  printf("Hello from Safari iOS WASM OOM reproducer!\n");
  return 0;
}

and an accompanying build script:

#!/bin/sh
emcc \
    -sENVIRONMENT=web \
    -sINITIAL_MEMORY=67108864 \
    -sALLOW_MEMORY_GROWTH=1 \
    -sABORTING_MALLOC=1 \
    index.c -o index.html

Repeatedly loading the resulting index.html in Safari on iOS 17.5.1 triggers the issue after a couple of refreshes, and crucially persists across reloads, necessitating a TAB REOPEN (edit: previously though browser needed to be restarted). I will submit this reproducer to the Webkit issues mentioned in the previous post.

Edit: I can not reproduce this in the Safari iOS 18 Developer Beta (22A5282m). Can anyone confirm?

This the issue also happen if you remove -sALLOW_MEMORY_GROWTH?

How about if you add -sMAXIMUM_MEMORY=100mb?

@suzukieng
Copy link

@sbc100
I ran it again with no memory growth and also with increased memory. Both did not help. I also adapted the reproducer to not use -sALLOW_MEMORY_GROWTH, I was unaware it was off by default.
Please let me know if I should test other things, I can very easily reproduce this.

This is what's being dumped to the console:

[Error] wasm streaming compile failed: RangeError: Out of memory
	(anonymous function) (index.js:708)
[Error] falling back to ArrayBuffer instantiation
	(anonymous function) (index.js:709)
[Error] failed to asynchronously prepare wasm: RangeError: Out of memory
	(anonymous function) (index.js:680)
[Error] Aborted(RangeError: Out of memory)
	abort (index.js:562)
	(anonymous function) (index.js:686)
[Error] Unhandled Promise Rejection: RuntimeError: Aborted(RangeError: Out of memory)

The playground is stuck in "preparing...".

emscripten_preparing

What's encouraging is that I can not reproduce the issue on Safari iOS 18 Developer Beta, so it might be fixed there. The release notes don't mention anything in that direction (https://developer.apple.com/documentation/safari-release-notes/safari-18-release-notes#Web-Assembly) but that's unsurprising to me.

From my view this looks like a browser bug. Maybe if you in your capacity as maintainer of Emscripten could add your voice to the WebKit issue, it might help raise awareness?

@suzukieng
Copy link

For what it's worth, I also tried lower memory values like 32MB or 16MB, the issue also happens, just takes longer to appear.

@sbc100
Copy link
Collaborator

sbc100 commented Jun 19, 2024

Yes, I agree this does sounds like a browser bug. If it really is fixed upstream already that would be great!

@suzukieng
Copy link

Good news: I can also no longer reproduce this in Safari on iOS 17.6 Beta (21G5052e). Can someone confirm? @ivancuric maybe?

@ivancuric
Copy link

Will be able to test on Monday.

@oatgnauh
Copy link

Hi all, how's this issue going? I met the same error om safari 17.5. my initialize memory is 64MB

@dr-matt
Copy link

dr-matt commented Aug 1, 2024

Now that ios safari 17.6 has been officially released, based on @suzukieng 's observation, can anyone independently confirm that this issue has been resolved / improved?

@suzukieng
Copy link

@dr-matt I tested again today on iOS 17.6 (21G80), and apparently the issue still exists (see comment on the WebKit issue). Would really appreciate if someone else can confirm the observation.

@suzukieng
Copy link

@ivancuric @MathisRaibaud have you seen progress on this issue since iOS 17.6? I am still able to reproduce it, although it happens less frequently. Also I have an iPhone 12 on the iOS 18 Public Beta where I have not been able to reproduce yet. It's a bit frustrating that there is no indication from the WebKit team regarding the status of the issue.

@MathisRaibaud
Copy link
Author

MathisRaibaud commented Aug 6, 2024

@suzukieng I've just checked and I'm still reproducing it in iOS 17.6.

@dr-matt
Copy link

dr-matt commented Aug 6, 2024

Thanks everybody for checking, I don't have access to a compatible device, but have had indirect user reports that agree it's not fixed. I'm hoping that this and growable shared memory are both resolved when iOS 18 is released in a month or two...

@MathisRaibaud
Copy link
Author

MathisRaibaud commented Sep 3, 2024

FYI, I've just tested using my reproduction repository with an iPhone 11 Pro on iOS 18 beta 8 (22A5350a), it seems fixed 🙏

I'll do another test when the official version of iOS 18 is available and let you know the result.

@dr-matt
Copy link

dr-matt commented Sep 23, 2024

It appears fixed to me in the officially released iOS 18.

@sbc100
Copy link
Collaborator

sbc100 commented Sep 23, 2024

Awesome! Closing this for now. Feel free to re-open as needed.

@sbc100 sbc100 closed this as completed Sep 23, 2024
@suzukieng
Copy link

suzukieng commented Sep 23, 2024

Just chiming in to confirm that the issue appears to be solved in iOS 18.0 also in my tests.

@MathisRaibaud
Copy link
Author

MathisRaibaud commented Sep 24, 2024

Same here, the bug seems to be fixed in iOS 18.0 🥳 Thanks everyone!

@ivancuric
Copy link

This issue is still present in safari Version 18.0.1 (20619.1.26.31.7):
https://www.youtube.com/watch?v=4W_ScbuM0HI

@suzukieng
Copy link

@ivancuric are you sure the memory doesn't get released eventually, when there's too much pressure? Are you saying that you can still trigger the Aborted(RangeError: Out of memory) crash?

@ivancuric
Copy link

ivancuric commented Oct 16, 2024

It usually ends up in a crashed or reloaded tab, or a different side effect — in my case, RangeError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': Out of memory at ImageData creation, as I'm processing a video feed.

Switching to a single-threaded build resolves this issue.

I'll make a repro sample.

@ivancuric
Copy link

Reported a new bug on https://bugs.webkit.org/show_bug.cgi?id=281657, as this seems to be an issue with using shared memory in workers. The example that exhibits the leak isn't even using Emscripten but https://github.com/RReverser/wasm-bindgen-rayon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests