Skip to content

Commit

Permalink
Merge pull request #3263 from zenhack/dev-shm
Browse files Browse the repository at this point in the history
Mount a tmpfs at /dev/shm in the sandbox.
  • Loading branch information
kentonv authored Apr 11, 2020
2 parents 8a29509 + 079e3f7 commit 6d57b3c
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 3 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ NODE_HEADERS=$(METEOR_DEV_BUNDLE)/include/node
WARNINGS=-Wall -Wextra -Wglobal-constructors -Wno-sign-compare -Wno-unused-parameter
CXXFLAGS2=-std=c++1z $(WARNINGS) $(CXXFLAGS) -DSANDSTORM_BUILD=$(BUILD) -DKJ_HAS_OPENSSL -DKJ_HAS_ZLIB -DKJ_HAS_LIBDL -pthread -fPIC -I$(NODE_HEADERS) -DKJ_STD_COMPAT
CFLAGS2=$(CFLAGS) -pthread -fPIC -DKJ_STD_COMPAT
LIBS2=$(LIBS) deps/libsodium/build/src/libsodium/.libs/libsodium.a deps/boringssl/build/ssl/libssl.a deps/boringssl/build/crypto/libcrypto.a -lz -ldl -pthread
# -lrt is not used by sandstorm itself, but the test app uses it. It would be
# nice if we could not link everything against it.
LIBS2=$(LIBS) deps/libsodium/build/src/libsodium/.libs/libsodium.a deps/boringssl/build/ssl/libssl.a deps/boringssl/build/crypto/libcrypto.a -lz -ldl -pthread -lrt

define color
printf '\033[0;34m==== $1 ====\033[0m\n'
Expand Down
21 changes: 19 additions & 2 deletions src/sandstorm/supervisor.c++
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,12 @@ void SupervisorMain::makeCharDeviceNode(
KJ_SYSCALL(mount(kj::str("/dev/", realName).cStr(), dst.cStr(), nullptr, MS_BIND, nullptr));
}

void mountTmpFs(const char *name, const char *dest) {
KJ_SYSCALL(mount(name, dest, "tmpfs",
MS_NOSUID | MS_NODEV,
"size=16m,nr_inodes=4k,mode=770"));
}

void SupervisorMain::setupFilesystem() {
// The root of our mount namespace will be the app package itself. We optionally create
// tmp, dev, and var. tmp is an ordinary tmpfs. dev is a read-only tmpfs that contains
Expand Down Expand Up @@ -986,8 +992,7 @@ void SupervisorMain::setupFilesystem() {
// 2) When we exit, the mount namespace disappears and the tmpfs is thus automatically
// unmounted. No need for careful cleanup, and no need to implement a risky recursive
// delete.
KJ_SYSCALL(mount("sandstorm-tmp", "tmp", "tmpfs", MS_NOSUID,
"size=16m,nr_inodes=4k,mode=770"));
mountTmpFs("sandstorm-tmp", "tmp");
}
if (access("dev", F_OK) == 0) {
KJ_SYSCALL(mount("sandstorm-dev", "dev", "tmpfs",
Expand All @@ -997,6 +1002,18 @@ void SupervisorMain::setupFilesystem() {
makeCharDeviceNode("zero", "zero", 1, 5);
makeCharDeviceNode("random", "urandom", 1, 9);
makeCharDeviceNode("urandom", "urandom", 1, 9);

// Create /dev/shm so shm_open() and friends work. Note that even though /dev
// is already a tmpfs, we need to mount a separate tmpfs for /dev/shm, because
// the former will be read-only.
//
// TODO: it might be nice to have /dev/shm and /tmp share the same partition,
// so we don't have to strictly separate their storage capacity. We could mount
// a single tmpfs somewhere invisible, create subdirectories, and then bind-mount
// them to their final destinations.
KJ_SYSCALL(mkdir("dev/shm", 0700));
mountTmpFs("sandstorm-shm", "dev/shm");

KJ_SYSCALL(mount("dev", "dev", nullptr,
MS_REMOUNT | MS_BIND | MS_NOEXEC | MS_NOSUID | MS_NODEV | MS_RDONLY,
nullptr));
Expand Down
40 changes: 40 additions & 0 deletions src/sandstorm/test-app/test-app.c++
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
#include <map>

#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <kj/main.h>
#include <kj/debug.h>
Expand Down Expand Up @@ -93,6 +97,39 @@ private:

// =======================================================================================

void testSystemApi() {
// Test that some syscalls & platform APIs work as expected. Print a success
// message to the console so the test suite can verify this.

std::cout << "Testing System APIs" << std::endl;

auto result = kj::runCatchingExceptions([]() {
// Test use of /dev/shm:
const char *obj_name = "/some-shm-obj";
int shm_fd;
KJ_SYSCALL(shm_fd = shm_open(obj_name, O_RDWR|O_CREAT, 0700));
KJ_DEFER(KJ_SYSCALL(shm_unlink(obj_name)));

// Make sure the mapping actually works:
int *mapped = (int *)mmap(
nullptr, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0
);
KJ_ASSERT(mapped != MAP_FAILED, "mmap() failed");
KJ_ASSERT(close(shm_fd) == 0, "Closing shm_fd failed");
KJ_SYSCALL(munmap(mapped, sizeof(int)));
});

KJ_IF_MAYBE(exception, result) {
auto msg = kj::str(*exception);
std::cout << msg.cStr() << std::endl;
throw(*exception);
}

std::cout << "testSystemApi() passed." << std::endl;
}

// =======================================================================================

class WebSessionImpl final: public sandstorm::WebSession::Server {
public:
WebSessionImpl(sandstorm::UserInfo::Reader userInfo,
Expand Down Expand Up @@ -148,6 +185,9 @@ public:
httpResponse.setMimeType("text/plain");
httpResponse.getBody().setBytes(response.getText().asBytes());
});
} else if(path == "test-system-api") {
testSystemApi();
return kj::READY_NOW;
} else if(path == "schedule") {
context.getResults().initNoContent();
// Put the extra headers in a map, so we can easily look for specific ones:
Expand Down
2 changes: 2 additions & 0 deletions src/sandstorm/test-app/test-app.html
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,7 @@ <h1>Test App</h1>

<p><button onclick="postScheduledJob({shouldCancel: false, refStr: 'oneshot'})" id="do-schedule-oneshot">
Schedule a one-shot job.</button></p>

<p><button onclick="doPost('/test-system-api', '')" id="do-test-system-api">Test the system api.</button></p>
</body>
</html>
47 changes: 47 additions & 0 deletions tests/tests/system-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Sandstorm - Personal Cloud Sandbox
// Copyright (c) 2020 Sandstorm Development Group, Inc. and contributors
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

"use strict";

const { short_wait } = require('../utils');

module.exports = {};

module.exports["Test system api"] = function(browser) {
const selector = "#do-test-system-api";
browser
.init()
.loginDevAccount()
.uploadTestApp()
.assert.containsText("#grainTitle", "Untitled Sandstorm Test App instance")
// Start opening this now, so we don't have to wait for it later when we
// want to use it:
.click("#openDebugLog")
.grainFrame()
.waitForElementPresent(selector, short_wait)
.click(selector)
.pause(short_wait)
.windowHandles(windows => browser.switchWindow(windows.value[1]))
.waitForElementVisible(".grainlog-contents > pre", short_wait)
.assert.containsText(".grainlog-contents > pre", "testSystemApi() passed.")

// Close the grain log, and switch back to to the main window, to avoid
// confusing future tests:
browser.windowHandles(windows => {
browser.closeWindow()
browser.switchWindow(windows.value[0])
})
}

0 comments on commit 6d57b3c

Please sign in to comment.