Medium / phantomjs

NPM wrapper for installing phantomjs
Other
1.42k stars 436 forks source link

global binary + installing phantomjs-prebuilt globally & locally == fork bomb? #701

Open markstos opened 7 years ago

markstos commented 7 years ago

Fun times.

I believe the following elements in combination may be responsible for triggering a kind of fork bomb on our CI server, in which processes running phantomjs --version were spawned until all memory was consumed and the server crashed. See attached screenshot:

phantom-fork-bomb

Here's some snippets from our Dockerfile which set up an environment to trigger the issue:

FROM ubuntu:14.04
ENV PHANTOM_JS="phantomjs-2.1.1-linux-x86_64"
RUN wget https://github.com/Medium/phantomjs/releases/download/v2.1.1/$PHANTOM_JS.tar.bz2 \
  && tar xvjf $PHANTOM_JS.tar.bz2 \
  && mv $PHANTOM_JS /usr/local/share \
  && ln -sf /usr/local/share/$PHANTOM_JS/bin/phantomjs /usr/local/bin

ENV NODE_VERSION 4.2.6
RUN wget "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && wget "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

RUN gpg --homedir /root --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
    && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c -

RUN tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs;

ENV YARN_VERSION 0.24.5
ENV PHANTOM_VERSION 2.1.13

# These installs happen as root, but use /home/jenkins/.yarn to avoid permissions problem
# with storing files under /root, which is not only accessible by root.
# Ref: https://github.com/yarnpkg/yarn/issues/1194#issuecomment-261379289
ENV HOME /home/jenkins
RUN npm install yarn@$YARN_VERSION --global \
 && yarn global add node-gyp phantomjs-prebuilt@$PHANTOM_VERSION 

In that environment yarn install --force for project would trigger the issue. I presume the key part was the presence of phantom-render-stream in our package.json, which in turn depends on phantomjs-prebuilt.

From reviewing the output of yarn install --force --verbose, the problem doesn't seem to block the installation, but continues to run wild in the background like a bull in a china shop.

Background

At first, we weren't pre-installing /usr/local/bin/phantomjs manually, but we using yarn global add phantomjs-prebuilt. However, we found this didn't install it in the "global" place that we wanted. Rather, it put the binary into /home/jenkins/.yarn-config. This broke things when we rsync'ed the project the production servers which expected a working reference to /usr/local/bin.

That's when we added the step to manually install phantomjs into /usr/local/bin, which added the final piece to trigger the issue. Our solution was to remove the yarn global add phantomjs-prebuilt step, which wasn't needed anymore.

That screenshot again

Let's look at that screenshot again. There's some interesting stuff in there:

phantom-fork-bomb

First, see the issue definitely originates in install.js. Also note that every call to phantomjs --version is not the same. The third one starts with /usr/bin/env, the others do not. (Why?). We also see that these processes seem to never exit. The number only goes up until the server crashes. Finally, we also see an infinite-depth of child processes attemped to be created but also a stream of sibling processes being created as well.

Conclusions?

Don't use yarn global add phantomjs-prebuilt.

I took a peek at the source code, and nothing jumped out at me right away.

Maybe there are some kew calls that need a .fin call to clean up some resources?

Maybe there are potential bad interactions when phantomjs-prebuilt is installed both globally and locally?

Maybe yarn's method of "global" package installation plays a role?

I'm not sure what's reasonable to be done about this, the case seemed "interesting" so I thought I would report it.