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

[🐛 Bug]: WebAssembly.instantiate(): Wasm code generation disallowed by embedder #704

Closed
1 task done
davidtranjs opened this issue Mar 14, 2024 · 14 comments
Closed
1 task done
Labels
bug Something isn't working

Comments

@davidtranjs
Copy link

next-on-pages environment related information

System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 23.1.0: Mon Oct 9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
CPU: (10) arm64 Apple M1 Pro
Memory: 16 GB
Shell: /bin/zsh
Package Manager Used: npm (9.6.7)

Relevant Packages:
@cloudflare/next-on-pages: 1.9.0
vercel: N/A
next: N/A

Description

Hi everyone,

Thanks for this awesome library.

Currently I have an NextJS app that deployed on Cloudflare pages, my app have an API route for generate OG Image in PNG format.

I use satori for generate SVG data and @resvg/resvg-wasm for convert SVG to PNG format.

Here is my code at pages/api/og.tsx

import OgImageTemplate from "@/components/OgImageTemplate";
import satori from "satori";
import { Resvg, initWasm } from "@resvg/resvg-wasm";

export const runtime = "edge";

export default async function handler(req) {
  const fontRegular = await fetch(
    "https://cdn.jsdelivr.net/fontsource/fonts/poppins@latest/latin-400-normal.ttf"
  ).then((res) => res.arrayBuffer());

  const { searchParams } = new URL(req.url);
  const title = searchParams.get("title");
  const sub = searchParams.get("sub");

  const ogImage = await satori(<OgImageTemplate title={title} sub={sub} />, {
    width: 1200,
    height: 630,
    fonts: [
      {
        name: "Poppins",
        data: fontRegular,
        weight: 400,
        style: "normal",
      }
    ],
  });

  const wasmResponse = await fetch("https://unpkg.com/@resvg/[email protected]/index_bg.wasm");
  const wasmArrayBuffer = await wasmResponse.arrayBuffer();

  try {
    await initWasm(wasmArrayBuffer); // => this line throw the error

    const resvg = new Resvg(ogImage, {
      font: {
        loadSystemFonts: false,
      },
    });
    const pngData = resvg.render();
    const pngBuffer = pngData.asPng();

    return new Response(pngBuffer, {
      status: 200,
      headers: {
        "Content-Type": "image/png",
      },
    });
  } catch (error) {
    console.error("Resvg wasm not initialized => ", error);
    return new Response("Resvg wasm not initialized", { status: 500 });
  }
}

In local, it work as expect, but when I deploy to Cloudflare pages, but when I access the API, function initWasm keep throw error with message

CompileError: WebAssembly.instantiate(): Wasm code generation disallowed by embedder

Is there anyone know the solution for this ?

Live URL: https://008c691d.personal-doh.pages.dev/api/og?title=xyz

Reproduction

  1. Deploy to Cloudflare pages
  2. Trigger request to API route that generate og image - https://008c691d.personal-doh.pages.dev/api/og?title=xyz
  3. Check function error log in Cloudflare pages setting

Pages Deployment Method

Pages CI (GitHub/GitLab integration)

Pages Deployment ID

https://008c691d.personal-doh.pages.dev

Additional Information

No response

Would you like to help?

  • Would you like to help fixing this bug?
@davidtranjs davidtranjs added the bug Something isn't working label Mar 14, 2024
@JoepKockelkorn
Copy link

I'm running Qwik City on Vercel Edge and I'm using the framework agnostic og-image library https://github.com/fabian-hiller/og-img but I'm running into exactly the same error:

CompileError: WebAssembly.instantiate(): Wasm code generation disallowed by embedder
    at (q-C8RaJ7ou.js:119:7794)
    at (q-C8RaJ7ou.js:119:9924)
    at (q-C8RaJ7ou.js:119:10084)
    at (q-C8RaJ7ou.js:186:29695)

I guess this is a security measure to prevent loading external wasm files?

@dario-piotrowicz
Copy link
Member

Hi @JoepKockelkorn, thank you for the issue 🙂 (and for the nice words regarding the library ❤️)

Yes, wasm code generation is not supported in our runtime for security reasons, exactly like in JavaScript where eval and new Function are also not supported.

I can see that we don't document this in our docs, I've created an issue for that: cloudflare/cloudflare-docs#13469

I don't think there can be any possible workaround for this either 😓

@JoepKockelkorn
Copy link

Thanks for your clarification. Security above everything!

For the nice words you'll have to thank @dungmidside, I just ran into something similar on Vercel Edge 😉. But I'm sure cloudflare pages is great!

Thanks for creating the issue 👍

@dario-piotrowicz
Copy link
Member

For the nice words you'll have to thank @dungmidside

Sorry, I somehow missed that the comment was from a different user 😅🤦

@dungmidside thanks! ❤️

@dario-piotrowicz
Copy link
Member

@dungmidside @JoepKockelkorn since there isn't really much we can do about this here I would be inclined to close this issue, would that work for you? 🙂

@JoepKockelkorn
Copy link

Sure!

@davidtranjs
Copy link
Author

Hi @dario-piotrowicz, thanks for your response.

I just wonder about this, if cloudflare block wasm code generation, why this worker-og library - which use the same initWasm function, work fine ?

I have another worker that use this library and it worked even it seem use the same @resvg/resvg-wasm with my code in NextJS app

import { ImageResponse } from 'workers-og';

export default {
  async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const fontRegular = await fetch('https://cdn.jsdelivr.net/fontsource/fonts/poppins@latest/latin-400-normal.ttf').then((res) =>
      res.arrayBuffer()
    );

    const decodedUrl = req.url.replace(/&amp;/g, '&');
    const params = new URLSearchParams(new URL(decodedUrl).search);
    const title = params.get('title');

    const html = `<div style="">${title}</div>
    `;

    return new ImageResponse(html, {
      width: 1200,
      height: 630,
      fonts: [
        {
          name: 'Poppins',
          data: fontRegular,
          weight: 400,
          style: 'normal',
        },
      ],
    });
  },
};

@dario-piotrowicz
Copy link
Member

@dungmidside the code you shared imports the wasm modules from local files: https://github.com/kvnang/workers-og/blob/52489a562245fb01f10a38a7a1a523fc03497a1c/packages/workers-og/src/og.ts#L8-L11

So those modules are fine to be initialized, the issue if that the you cannot fetch the wasm modules from some external source (like you did with fetch("https://unpkg.com/@resvg/[email protected]/index_bg.wasm")) and initialize them (since that's not trusted code).

@davidtranjs
Copy link
Author

@dario-piotrowicz I used fetch because import wasmFile from './index_bg.wasm' not worked in NextJS
So this could solve by adapt NextJS config to able import *.wasm file ?

@dario-piotrowicz
Copy link
Member

@dungmidside yes exactly, you could update the config file to allow for wasm imports or more simply you could just import the wasm using import ... from '...?module'

Like I did here for example:

I've also just found out that there's an error page regarding this in the Next.js docs 🙂: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation

@davidtranjs
Copy link
Author

davidtranjs commented Mar 17, 2024

@dario-piotrowicz thanks for your suggestion about import with ?module
But when I deploy to Cloudflare Pages, it show this error. Is there any NextJS config changes for this to work ?


Build Completed in .vercel/output [30s]
--
23:05:21.143 | ⚡️ Completed `npx vercel build`.
23:05:21.741 | ⚡️ Unexpected error: ENOENT: no such file or directory, copyfile '/opt/buildhome/repo/.vercel/output/functions/api/og.func/wasm/wasm_3481987cfb7f2b7b841f3cf7520cc11c1d5434cd.wasm' -> '/opt/buildhome/repo/.vercel/output/static/_worker.js/__next-on-pages-dist__/wasm/wasm_3481987cfb7f2b7b841f3cf7520cc11c1d5434cd.wasm'
23:05:21.776 | Finished

I copy index_bg.wasm file in node_modules of @resvg/resvg-wasm and import wasmModule from './index_bg.wasm?module';, it worked in local

import { Resvg, initWasm } from "@resvg/resvg-wasm";
import wasmModule from './index_bg.wasm?module';
// other code
await initWasm(wasmModule);

@dario-piotrowicz
Copy link
Member

@dungmidside the error you shared is an error that should have already been fixed in: #681, which has been shipped as part of the 1.10.0 release.

From the PR description it seems like you're on 1.9.0?

Could you make sure to use 1.10.0 and let me know if that helps? 🙏

@davidtranjs
Copy link
Author

@dario-piotrowicz it seem work now
But now my worker is exceed 1MB : ((
Maybe because the wasm file of resvg is about 2.5MB already
Thanks a lot for your quick response 💯

@dario-piotrowicz
Copy link
Member

@dungmidside my pleasure, I'm glad we did get to the bottom of it 😄

regarding the limit yeah sorry about that, there's nothing I can really do about it 😓
but if you could try out the paid plan there the limit is 10MB 🙂 (https://developers.cloudflare.com/workers/platform/limits/#worker-size)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants