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

Add basic Intel SGX support #2219

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open

Add basic Intel SGX support #2219

wants to merge 38 commits into from

Conversation

hf
Copy link

@hf hf commented Jan 7, 2020

Duktape is great for embedded environments, and so I wanted to try and run it in Intel SGX. There is some special configuration needed and refactoring string formatting in a few places, but otherwise works just fine.

Summary of changes:

  1. Add a new platform platform_intelsgx.
    The order is important to be immediately above Linux and Windows. Intel SGX enclaves are compiled under Linux or Windows with a special C library (tlibc; trusted libc) but otherwise are indistinguishable from other object files. Therefore the Linux and Windows detection will trigger.
  2. Automatic detection that we're compiling an enclave is really tricky.
    There is no official documentation that describes if there is a macro you can check to see if compiling against tlibc; I did manage to find _TLIBC_CDECL_ and a few others from sys/cdefs.h which might be used for this purpose but I decided it's best to prefer manual versus automatic detection. In order to hint that this is the Intel SGX build either define INTELSGX manually or use --platform=intelsgx in the configure.py invocations.
  3. Intel SGX enclaves do not have access to syscalls, and therefore they can't get information about the current time and locale timezone offset. This makes it impossible to run Duktape without any intervention, but luckily this boils down to defining two macros DUK_USE_DATE_GET_NOW and DUK_USE_DATE_GET_LOCAL_TZOFFSET. These are mandatory and well documented, and compilation fails with a preprocessor error if they are not added.
  4. Intel SGX's tlibc does not include the "unsafe" string parsing and formatting functions. It only uses the "safe" ones. Meaning that all uses of DUK_SPRINTF needed to be refactored into DUK_SNPRINTF, but that was an easy affair. In general it's my opinion that going forward the unsafe functions be completely "banned" from the Duktape code, and be replaced with safe ones.
  5. Add DUK_USE_STANDARDIZED_POINTER_ENCODING option (default: false) that encodes all pointers in a "standardized" way i.e. in lowercase hexadecimal encoding as the value is laid out in memory. In order to implement this, the functions duk_encode_pointer_cstr and duk_decode_pointer_cstr were added to duk_util_misc.c. They have also been replaced wherever the %p format specifier was used directly or indirectly (except for debug statements, of course). By enabling this option, Duktape no longer uses DUK_SSCANF.
  6. Added a few things to .gitignore, most importantly runtests/package-lock.json which is added by npm when tests are run locally. It may be a good idea to remove this from .gitignore if we want to pin down versions.
  7. Changed invocation of python within runtests/runtests.js to python2.7 since running the ecmatest with Python 3 failed with error. These days Python 3 is behind python, while 2.7 can be invoked with python2.7.

This PR is not complete I'm still testing and changing things, but discussions can begin IMO.

Remaining things to complete:

  • apitest fails when DUK_USE_STANDARDIZED_POINTER_ENCODING because of the hardcoded expectation for 0xdeadbeef. ecmatest succeeds, tho. Possible solutions:
    1. Test expectations get passed through jade or some other preprocessor so they can be configured before being run.
    2. All 0xdeadbeef occurrences in expectations within tests/api are replaced with a configurable value.
    3. Change the standardized format, though I really like the simplicity of 1:1 memory layout in hex.
  • Add new doc page for how to compile for Intel SGX.
  • Write instructions for testing.

config/platforms/platform_intelsgx.h.in Outdated Show resolved Hide resolved
config/platforms/platform_intelsgx.h.in Outdated Show resolved Hide resolved
src-input/duk_bi_date.c Outdated Show resolved Hide resolved
src-input/duk_bi_date.c Outdated Show resolved Hide resolved
@hf hf force-pushed the hf/sgx-support branch 4 times, most recently from d0ff414 to f16266b Compare January 8, 2020 21:29
@hf
Copy link
Author

hf commented Jan 8, 2020

Tests are failing because conditional undef DUK_USE_JX is not possible. The configuration tools read the undef but not the elif defined(DUK_F_INTELSGX) surrounding it.

@hf hf force-pushed the hf/sgx-support branch from 33065f9 to 808dd4c Compare January 8, 2020 23:05
@hf
Copy link
Author

hf commented Jan 8, 2020

Here are some possible solutions to the problem:

  1. Change reading and formatting pointers to functions for that purpose:
    #define DUK_PTR2STR(buf,sz,ptr) DUK_SNPRINTF(buf,sz,"%p",ptr)
    #define DUK_STR2PTR(buf,sz,ptr) DUK_SSCANF(buf,"%p",ptr)
    Then these can be set to different values for Intel SGX or other constrained environments.
  2. Knowingly leave the DUK_SSCANF use in DUK_USE_JX sections, but add documentation and example configuration that disables DUK_USE_JX for Intel SGX.
  3. Define platform independent encoding of pointers without the use of printf and scanf families.

@svaarala
Copy link
Owner

Thanks, interesting stuff! I'll look into the changes as soon as I get a chance.

I agree banning plain sprintf() would be nice. The reason sprintf() is currently used is that snprintf() has portability issues, e.g. it's not available on legacy platforms and sometimes interpretations before C99 differ, especially handling of truncation. So there are pros and cons in each case.

I'd be OK with requiring snprintf(), it's probably the better compromise. Users running pre-C99 would then need to implement or borrow one but that's not a big deal. This could be made easier by making it easy to use the minimal replacements https://github.com/svaarala/duktape/tree/master/extras/minimal-printf via config.

@hf
Copy link
Author

hf commented Jan 11, 2020

I decided to add a new option DUK_USE_STANDARDIZED_POINTER_ENCODING which when enabled will encode pointers in a "standard" way, i.e. read the pointer value as it is laid out in memory and write it to a string in lowercase hex.

This PR is getting a little large, maybe once we agree on things I can break it up into smaller ones.


for (i = 0; i < sz; i++) {
if (0 == buf[i]) {
goto safe_sscanf;
Copy link
Author

@hf hf Jan 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to add this safety check for sscanf because previously there was none, and there could have been cases where, for eg. { "__ptr": " " } was passed, and since sscanf and %p are generally available, but the %p is not specified meaning bad implementations may start reading the JX buffer until the end. This way, that can't ever happen.

src-input/duk_api_stack.c Outdated Show resolved Hide resolved
src-input/duk_util_misc.c Outdated Show resolved Hide resolved
@svaarala
Copy link
Owner

Re: forcing DUK_F_INTELSGX from outside, there are existing platforms where automatic detection is not possible and an external define is used to force detection, see for example https://github.com/svaarala/duktape/blob/master/config/helper-snippets/DUK_F_AMIGAOS.h.in (for VBCC).

It would be best for the define not to have a DUK_F_ prefix, because the configuration model uses those defines purely for internal purposes. Using just -DINTELSGX would be consistent with handling of e.g. Amiga + VBCC.

But if it would be clearer, maybe forced platforms could be handled using a convention like DUK_PLATFORM_AMIGA, DUK_PLATFORM_INTELSGX, etc.

@svaarala
Copy link
Owner

I'll try to review the diff tomorrow night, assuming it is ready for comments.

@hf
Copy link
Author

hf commented Jan 24, 2020

Yes, ready for comments @svaarala.

I agree on the point for DUK_F_INTELSGX and will produce changes toward that approach this weekend.

doc/json.rst Outdated Show resolved Hide resolved
@hf hf requested a review from svaarala January 25, 2020 17:12
AUTHORS.rst Outdated Show resolved Hide resolved
config/platforms/platform_intelsgx.h.in Outdated Show resolved Hide resolved
runtests/runtests.js Outdated Show resolved Hide resolved
src-input/duk_util.h Outdated Show resolved Hide resolved
src-input/duk_util.h Outdated Show resolved Hide resolved
src-input/duk_util_misc.c Outdated Show resolved Hide resolved
src-input/duk_util_misc.c Outdated Show resolved Hide resolved
src-input/duk_util_misc.c Show resolved Hide resolved
@svaarala
Copy link
Owner

@hf I finally got some time to do a review on the changes. Mostly just trivia but there's one probable snprintf() truncation issue and AUTHORS needs a fix. With these fixed and the commits squashed this is ready for merging. Thanks for the detailed work! 👍

@hf
Copy link
Author

hf commented Feb 1, 2020

@svaarala Thanks for reviewing. I tried to get all indent issues, but not sure how I missed these. Maybe think about setting up clang-format to get rid of issues like these?

I'll investigate more about the snprintf, and adjust code so that it works good. I like the MEMBASED name more, so I'll go with that one.

@hf
Copy link
Author

hf commented Feb 4, 2020

What's the ideal style for void**? 😅
Is it void* *?

@hf
Copy link
Author

hf commented Feb 5, 2020

Still many broken indents. Something is wrong with my vim, it's confused by this codebase.

@sva-p
Copy link
Contributor

sva-p commented Feb 6, 2020

What's the ideal style for void**?

In the current codebase it would simply be void **.

@svaarala
Copy link
Owner

svaarala commented Feb 12, 2020

@svaarala Thanks for reviewing. I tried to get all indent issues, but not sure how I missed these. Maybe think about setting up clang-format to get rid of issues like these?

I've gone through uncrustify, astyle, and clang-format, and none of them have enough controls to get the results I would want (which is not surprising). But it's probably best to compromise on the result so that code style would be automatic, so I'll merge in something for 3.0.x release.

This was one major issue: https://stackoverflow.com/questions/38620019/can-clang-format-align-a-block-of-defines-for-me. It's fixed in clang-format-9 but that may not be available on the host so the autoindenting step will need to be dockerized.

@hf
Copy link
Author

hf commented Feb 12, 2020

Also a good idea is to add .editorconfig since this codebase uses tabs (and I prefer spaces, but that is irrelevant). My vim struggled a bit, so I did a refactor of my dotfiles to fix it. :)

@svaarala
Copy link
Owner

Adding a .editorconfig sounds good to me. I'll steer clear of debating tabs vs spaces ;-)

@hf
Copy link
Author

hf commented Feb 29, 2020

Hey @svaarala I finally managed to fix all outstanding issues. Would you mind checking the logic I used in the unresolved comments before merging?

TODOs post PR:

  1. Add basic Intel SGX support #2219 (comment)

@svaarala
Copy link
Owner

I'll take a look - after that commits should be squashed for merging (a single commit is fine).

skip reliance on sscanf for platforms that don't support it.

When turned off, pointer encoding to string is done via the %p format
specifier in the printf and scanf family of functions.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be removed from the squashed commit.

@@ -0,0 +1,9 @@
/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivia: no double star in comment style.

#undef DUK_USE_DATE_TZO_GMTIME_R
#undef DUK_USE_DATE_TZO_GMTIME_S
#undef DUK_USE_DATE_TZO_WINDOWS
#undef DUK_USE_DATE_TZO_WINDOWS_NO_DST
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no need to undefine any of these. Only platform files are intended to define the Date provider defines.

* DUK_USE_DATE_GET_LOCAL_TZOFFSET for their enclave either manually
* in duk_config.h or via other means (other config header, compiler
* flags). If these are not defined compilation will fail with a
* preprocessor error.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just say "in duk_config.h", no other means are supported. So something like:

Enclave developers must provide DUK_USE_DATE_GET_NOW and DUK_USE_DATE_GET_LOCAL_TZOFFSET when configuring Duktape for build.

* Intel SGX enclaves don't have access to sscanf, so pointer parsing must
* be without using sscanf.
*/
#define DUK_USE_MEMBASED_POINTER_ENCODING
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably won't work as expected: if this define appears here, it will prevent the emission of the default value for all platforms when duk_config.h default/forced options section is emitted. The problem is that the default/forced options section only knows that the define is forced in the preceding header, but it doesn't know (or care) that it is conditional to a platform.

So IMO this header should just say in its comments that DUK_USE_MEMBASED_POINTER_ENCODING is a required option for Intel SGX enclaves. There should be a better way to address this, but at present I don't think configure.py can do better.

One possible solution would be for platforms to optionally have a config check snippet at the end of duk_config.h where they could check that required options are met, so that they could #error out cleanly.

* snprintf does not always terminate with NUL, however if it does it will
* write the NUL in the C position, which can be overwritten with 0xff
* if desc is NULL. Nonetheless, snprintf when positive, returns the number
* of bytes that the format would have taken without counting NUL.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent is broken here.

*
* A len B f f f f f f f f - f f f f f f f f C
* -- --- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* 00 len 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This layout seems a bit confusing, the offsets in the bottom row are not continuous (due to len).

/* make sure that we had enough room to format the symbol */
DUK_ASSERT(flen > 0 && flen <= 1 + 17 /* without + 1 for NUL */);

p += flen;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block has broken indent.

@@ -91,16 +91,40 @@ DUK_LOCAL void duk__bi_print(const char *name, duk__bigint *x) {
char buf[DUK__BI_MAX_PARTS * 9 + 64];
char *p = buf;
duk_small_int_t i;
duk_int_t flen;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken indent.

p += 63;
} else if (flen > 0) {
p += flen;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had forgotten about the possible negative error return value :o

Negative returns are probably not addressed correctly elsewhere. But it doesn't need to be addressed in this pull.

(Easiest fix would be to have an internal duk_snprintf() helper which would post-process the snprintf() return value: just convert <0 to 0, and cap the return value to bufsize - 1. The return value could then safely be used as is to increment pointers.)

@svaarala
Copy link
Owner

@hf Went through the changes and there's just some minor trivia left, see review comments.

Once you address them, you can just squash the commits into one, and I'll then merge the changes. I'll fix up whatever trivia is left while working on the snprintf() handling generally.

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

Successfully merging this pull request may close these issues.

3 participants