Skip to content

i#2140 : mixing syscall methods#2476

Open
Simorfo wants to merge 13 commits intoDynamoRIO:masterfrom
Simorfo:int2esys
Open

i#2140 : mixing syscall methods#2476
Simorfo wants to merge 13 commits intoDynamoRIO:masterfrom
Simorfo:int2esys

Conversation

@Simorfo
Copy link
Copy Markdown
Contributor

@Simorfo Simorfo commented Jun 16, 2017

Adds a simple test using int 2e for syscall, even if another method is used.

What is the best fix ?
Allowing multiple syscall methods ?

Comment thread core/arch/interp.c Outdated
}

#ifdef X86
if (my_dcontext != NULL && instr_is_syscall(bb->instr) &&
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do you check "my_dcontext"? The other code here does not do that. If it is necessary please add a comment explaining why.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I forgot to remove it after trying another solution.
I am removing it now

Comment thread core/arch/interp.c Outdated

#ifdef X86
if (my_dcontext != NULL && instr_is_syscall(bb->instr) &&
instr_get_opcode(bb->instr) == OP_int &&
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd prefer to avoid adding code to the main routine here: either this should be in mangling, if it's something that should happen after a client sees the unchanged code, or it should be in one of the syscall/interrupt handler routines already called during bb building.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, there is a good question that relates to my broader question of how to fix this case.

Should we relax the constraint in arch.c :

/* we assume only single method; else need multiple do_syscalls */
    ASSERT(new_method == get_syscall_method());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can see that the UNIX case right above what you're quoting does relax it and has do_int_syscall vs the sysenter do_syscall. We can do something similar for Windows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will look into it.
Do you know why this was done for Unix and not for Windows ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It looks like I should compute both do_syscall (int and other) method at initialization since the do_syscall method is written in the .data section, so it cannot be updated on Windows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

.data can be written later by temporarily unprotecting the target page (while trying to avoid races) -- there are some helpers for this like SELF_UNPROTECT_DATASEC

#else
pExceptionInfo->ContextRecord->Rip++;
#endif
#if 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This "#if 0" is accidentally left here? If this is deliberate, better to be under something descriptive like #ifdef VERBOSE

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Cool, I am changing it

pExceptionInfo->ContextRecord->Rip++;
#endif
#if 0
//prints information about exception
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: space after //

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

Comment thread core/arch/interp.c Outdated
get_syscall_method() != SYSCALL_METHOD_INT) {
instr_t * i2 = INSTR_CREATE_nop(dcontext);
SYSLOG_INTERNAL(SYSLOG_WARNING,
"Changing interruption to nop at "PFX"\n", bb->instr_start);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do not understand: why would we turn it into a nop?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That was the first POC I could find.
But then, it is clearly not transparent to the application.

if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
/* Go to next instruction. */
#ifndef X64
pExceptionInfo->ContextRecord->Eip++;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're assuming the instruction is a single byte? Please explain in a comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, it looks like (using the verbose output of the program) EIP got already incremented by one before the call of error handler.

I get exception code=c0000005 address=d21175 eip=d21176, info = 0, ffffffff

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

An access violation will point directly at the faulting instruction. Something is off here, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do not fully understand what is going on, but it looks like the printed EIP from the context in exception handler does not point to the faulting instruction, but rather at the address+1. I run the program directly in this case (no dynamorio).
This look like with an int 2d, except EIP got incremented instead of ExceptionAddress.

Can you reproduce this behavior by running security-win32.except-int2e.exe ?

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 11, 2017

After testing, it looks like the right solution is to prepare multiple routines for the different sys calls cases.

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 12, 2017

Here is another patch.
Instead of trying to do syscalls with multiple methods or updating do_syscall, I consider int 2e not as a syscall when another syscall method is used.
It works on the simple test, but I do not know if it covers all the cases.

@fhahn
Copy link
Copy Markdown
Contributor

fhahn commented Jul 12, 2017

run aarch64 tests

@fhahn
Copy link
Copy Markdown
Contributor

fhahn commented Jul 12, 2017

run aarch64 tests

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 12, 2017

I do not have an aarch64 platform to run tests.
This patch is only about X86, behavior for ARM should remain unchanged.

@fhahn
Copy link
Copy Markdown
Contributor

fhahn commented Jul 12, 2017

Ah sorry for the confusion. I've added a machine to run the precommit suite on AArch64 for pull requests, and a build is triggered by commenting run aarch64 tests . For some reason this PR does not get picked up, maybe because it was created before the machine started operating or because the branch is out-of-date.

I only tried to trigger a build, because of 91cf77d#diff-e1b5b4d92663976c0db6c7ccc36d578fR110 which might have an impact on ARM/AArch64?

@derekbruening
Copy link
Copy Markdown
Contributor

@fhahn -- it's probably best to document that "run aarch64 tests" and other info in the wiki

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 12, 2017

@fhahn ok I understand.
If I got it correct, 91cf77d#diff-e1b5b4d92663976c0db6c7ccc36d578fR110 should have no impact on ARM.

@derekbruening
Copy link
Copy Markdown
Contributor

Haven't looked at the code, but this confuses me:

I consider int 2e not as a syscall

We can't just ignore int 2e syscalls by the app, right, so I'm not sure what you mean?

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 19, 2017

We can't just ignore int 2e syscalls by the app, right, so I'm not sure what you mean?

We consider them as interruptions, not syscalls.
I am not fully aware of how sys calls are handled by dynamorio.
But so far, this patch seems to work on the tests cases, including the one I added about int 2e that did not work previously.

The code simply replaces in the good spots instr_is_syscall(i) by

instr_is_syscall(i) && 
(get_syscall_method() == SYSCALL_METHOD_INT || instr_get_opcode(i) != OP_int)

@derekbruening
Copy link
Copy Markdown
Contributor

We can't just ignore int 2e syscalls by the app, right, so I'm not sure what you mean?

We consider them as interruptions, not syscalls.
I am not fully aware of how sys calls are handled by dynamorio.

DR must see certain syscalls (control or memory related) for correct operation. Not monitoring some syscalls without knowing the syscall number and whether DR cares about it is unsafe. DR promises that clients will see all syscalls. Something like Dr. Memory or any taint tracker will have false positives and false negatives if it misses syscalls.

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Jul 20, 2017

Ok, so the test case is not complete enough and the patch is not good enough.
Can we translate an "int 2e" syscall into another type of sys call such as wow64 ?

@derekbruening
Copy link
Copy Markdown
Contributor

I would think we should leave the app syscalls using their original gateway methods. DR does have support for multiple app syscall gateways on Linux: e.g., see get_do_int81_syscall_entry and EXIT_REASON_NI_SYSCALL_INT_0x81 and ditto for 82, and using both sysenter and int 0x80. Presumably we can do the same thing on Windows.

The messy part is what to do when DR needs to fabricate a syscall that wasn't there in the app code. That's where there needs to be a single "primary" syscall method that DR will use. This is the syscall_method global state stuff. We just need to relax the checks there and pick one, like we do on Linux.

1 similar comment
@derekbruening
Copy link
Copy Markdown
Contributor

I would think we should leave the app syscalls using their original gateway methods. DR does have support for multiple app syscall gateways on Linux: e.g., see get_do_int81_syscall_entry and EXIT_REASON_NI_SYSCALL_INT_0x81 and ditto for 82, and using both sysenter and int 0x80. Presumably we can do the same thing on Windows.

The messy part is what to do when DR needs to fabricate a syscall that wasn't there in the app code. That's where there needs to be a single "primary" syscall method that DR will use. This is the syscall_method global state stuff. We just need to relax the checks there and pick one, like we do on Linux.

Copied code from int 81 handling on Linux.
Checks syscall param_base readability before logging.
Translates exception address in case exception is caused by int 2e.

Fixes issue 2140
Comment thread core/arch/arch.c Outdated
#else
/* we assume only single method; else need multiple do_syscalls */
ASSERT(new_method == get_syscall_method());
/* There is usually a single method. */
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Check needed to be relaxed

Comment thread core/arch/interp.c
}
}
#ifdef WINDOWS
if (instr_get_opcode(bb->instr) == OP_int) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe it is not the best style

Comment thread core/win32/callback.c
cxt->CXT_XIP = (ptr_uint_t) dcontext->asynch_target;
/* now handle the fault just like RaiseException */
DODEBUG({ known_source = true; });
} else if ((app_pc) pExcptRec->ExceptionAddress ==
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Syscall can cause exception (the test case does this).
So we need to translate the address from the do_syscall method to the program address where the sys call happens

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't follow: the syscall itself should not cause an exception: it would just return a failure return value, not throw an exception. Could you elaborate on why an exception would happen whose PC is the entry to our generated int 0x2e?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, I do not know what it should make, but the syscall does throw an exception, see the test case.
I guess that this happens because the syscall parameters are invalid (0xdeadc0de)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't make sense to me: this has never happened before. Invalid parameters result in the kernel returning an appropriate error code, not throwing an exception into user mode. Is this limited to WOW64 and this happens during the syscall argument marshalling in (64-bit) user mode and that's why there's an exception, because it's not yet in the kernel?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But you're saying this exception is on an int 0x2e, not a WOW64 "syscall" gateway of the call* to the WOW64 library layer.

Comment thread core/win32/syscall.c Outdated
sys_param(dcontext, param_base, 7));
LOG(THREAD, LOG_SYSCALLS, 3, "\tparam 8: "PFX"\n",
sys_param(dcontext, param_base, 8));
/* Needs to check readability before logging. */
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Check is needed because the program can set param_base to an invalid value (unreadable address in memory) and that happens with the test case

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sure, though this method is racy as the memory could become unreadable concurrently with this code. The more robust method is a safe_read or try/except, but since this is just logging code this is ok if it makes the code simpler: but please use a comment stating we're aware of the race.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Aug 22, 2017

So, here is a new patch, inspired by int 81 handling on Linux.
There was more to patch to get the test case working : check syscall param_base readability, and translate exception address caused by int 2e syscall.

Copy link
Copy Markdown
Contributor

@derekbruening derekbruening left a comment

Choose a reason for hiding this comment

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

Looks pretty good, just a few requests/suggestions

Comment thread core/arch/arch.c Outdated
/* we assume only single method; else need multiple do_syscalls */
ASSERT(new_method == get_syscall_method());
/* There is usually a single method. */
ASSERT_CURIOSITY(new_method == get_syscall_method());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since we handle it maybe there's no reason to print out a curiosity. But we still only handle combining syscall or sysenter with int 2e: we won't handle both syscall and sysenter (seems really unlikely of course), so maybe this should be a check that either the new or the old is int 2e, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

Comment thread core/arch/interp.c Outdated
/* FIXME i#1551: maybe we should union int80 and svc as both are inline syscall? */

#ifdef WINDOWS
/* Like int 81 on Linux. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

81 is on Mac, not Linux

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

Comment thread core/arch/interp.c
#ifdef WINDOWS
if (instr_get_opcode(bb->instr) == OP_int) {
if (instr_get_interrupt_number(bb->instr) == 0x2e) {
/* Special handling of int 2e when it is not the syscall method. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So the assumption is that syscall is seen first, and is the primary method? So that should be checked in check_syscall_method, right? B/c we won't handle the other order (right?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes.
To be more precise, "int" is the second method. Wow64 can be the first method as well.

Comment thread core/arch/x86/mangle.c Outdated
PRE(ilist, instr,
instr_create_save_immed16_to_dcontext(dcontext, reason,
EXIT_REASON_OFFSET));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see several instances of this if..else sequence now for writing the exit reason: how about putting it into a local helper function and calling that from all the cases in this file?

Copy link
Copy Markdown
Contributor Author

@Simorfo Simorfo Sep 13, 2017

Choose a reason for hiding this comment

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

ok done with mangle_special_exit

Comment thread core/arch/x86/mangle.c Outdated
instr_create_save_immed16_to_dcontext(dcontext, reason,
EXIT_REASON_OFFSET));
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

assert otherwise (i.e., if num is not 0x2e)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

Comment thread core/win32/callback.c
cxt->CXT_XIP = (ptr_uint_t) dcontext->asynch_target;
/* now handle the fault just like RaiseException */
DODEBUG({ known_source = true; });
} else if ((app_pc) pExcptRec->ExceptionAddress ==
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't follow: the syscall itself should not cause an exception: it would just return a failure return value, not throw an exception. Could you elaborate on why an exception would happen whose PC is the entry to our generated int 0x2e?

Comment thread core/win32/syscall.c Outdated
sys_param(dcontext, param_base, 7));
LOG(THREAD, LOG_SYSCALLS, 3, "\tparam 8: "PFX"\n",
sys_param(dcontext, param_base, 8));
/* Needs to check readability before logging. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sure, though this method is racy as the memory could become unreadable concurrently with this code. The more robust method is a safe_read or try/except, but since this is just logging code this is ok if it makes the code simpler: but please use a comment stating we're aware of the race.

Comment thread core/win32/syscall.c Outdated
LOG(THREAD, LOG_SYSCALLS, 3, "\tparam 8: "PFX"\n",
sys_param(dcontext, param_base, 8));
/* Needs to check readability before logging. */
if (is_readable_without_exception((byte *) param_base, 8 * sizeof(app_pc))) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The check should be inside a DOLOG to avoid waste when logging is turned off

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Sep 13, 2017

Here comes a new patch.

The important point is that syscall in the test case generates an exception (read access violation). So dynamoRIO indeed needs to translate the address when it catches an exception caused by a syscall routine.

@derekbruening
Copy link
Copy Markdown
Contributor

Sorry for the delay, but Travis did an upgrade and broke a lot of our tests (#2632, #2634, #2641) and we're still trying to fix them all. Also I am curious about this exception and plan to run it myself to see it firsthand when I get a chance.

@Simorfo
Copy link
Copy Markdown
Contributor Author

Simorfo commented Oct 2, 2017

Maybe this exception has something to do with code in syscall.c
IF_X64(ASSERT_TRUNCATE(sysnum, int, mc->xax));

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.

4 participants