|
From: Florian K. <br...@ac...> - 2012-02-22 22:45:47
|
Hello,
I was doing an experiment with some hand-written assembler program for
which I was expecting a memcheck complaint:
_start: lghi %r0,0 # r0 = 0
cgr %r2,%r0 # r2 == r0 ?
jz EXIT # if ....
EXIT: svc 1 # terminate process
No complaint was issued. The reason is that the dirty helper that is
instrumented in right before the conditional jump was not invoked. Which
means that the condition under which it is invoked was 0.
The condition was: vex_shadow1[r2] != 0
This is because in VG_(ii_finalise_image) we do this:
/* Zero out the shadow area. */
VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestS390XState));
VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestS390XState));
And that does not look right. At least on s390 all registers with
exception of stack pointer and such are assumed to have undefined
contents. I suspect this is similar on other architectures.
Two questions:
(1) Is the zero-out done on purpose? Perhaps according to the rationale:
by the time execution reaches those parts of the application
program a user cares about, glibc will have populated the registers
with some defined values anyhow? So why bother here?
(2) If I wanted to initialize those shadow areas to mark the registers
as uninitialized, what bit pattern should be stored? All bits 1?
Thanks,
Florian
|
|
From: John R. <jr...@bi...> - 2012-02-23 01:26:14
|
> /* Zero out the shadow area. */
> VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestS390XState));
> VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestS390XState));
>
> And that does not look right. At least on s390 all registers with
> exception of stack pointer and such are assumed to have undefined
> contents. I suspect this is similar on other architectures.
>
> Two questions:
>
> (1) Is the zero-out done on purpose?
So that memcheck itself does not use uninit bits.
Perhaps according to the rationale:
> by the time execution reaches those parts of the application
> program a user cares about, glibc will have populated the registers
> with some defined values anyhow? So why bother here?
For the actual answer, which is highly platform-dependent, see
<linux>/fs/binfmt_elf.h and the macro call:
ELF_PLAT_INIT(regs, reloc_func_desc);
On some $ARCH, execve() actually defines quite a few registers.
>
> (2) If I wanted to initialize those shadow areas to mark the registers
> as uninitialized, what bit pattern should be stored? All bits 1?
Hopefully the best answer will involve symbolic constants such as
V_BITS32_UNDEFINED or V_BITS64_UNDEFINED; see memcheck/memcheck.h.
--
|
|
From: John R. <jr...@bi...> - 2012-02-23 01:41:36
|
> Perhaps according to the rationale: >> by the time execution reaches those parts of the application >> program a user cares about, glibc will have populated the registers >> with some defined values anyhow? So why bother here? > > For the actual answer, which is highly platform-dependent, see > <linux>/fs/binfmt_elf.h and the macro call: > ELF_PLAT_INIT(regs, reloc_func_desc); > On some $ARCH, execve() actually defines quite a few registers. As a matter of information security (not leaking any data from anywhere else), the kernel must set all user-visible registers to something, probably zero (before ELF_PLAT_INIT chooses something else.) -- |
|
From: Julian S. <js...@ac...> - 2012-02-23 08:30:16
|
> (1) Is the zero-out done on purpose? Perhaps according to the rationale: > by the time execution reaches those parts of the application > program a user cares about, glibc will have populated the registers > with some defined values anyhow? So why bother here? Well .. so the zero-out for the other architectures .. I did that because from a conservatism point of view. Getting an initial state that works reliably has been difficult (on ppc64-linux I had mucho trouble) so zeroing everything seemed simplest. > (2) If I wanted to initialize those shadow areas to mark the registers > as uninitialized, what bit pattern should be stored? All bits 1? Yeah, 0 = defined, 1 = undefined. ------ Once the system is up and running and in "steady state", all undefinedness comes from either heap allocation, stack allocation or state changes caused by system calls. Making the registers undefined at process start will therefore at best find you uninit value uses only up to the point where they first become defined, which should happen very soon after startup. So .. apart from the curiosity value, I am not sure what this will buy you in the general case. Maybe you are doing some bizarre experiment with your handwritten assembly version of _start? J |
|
From: Florian K. <br...@ac...> - 2012-02-23 14:53:45
|
On 02/23/2012 03:28 AM, Julian Seward wrote:
> So .. apart from the curiosity value, I am not sure what this will buy
> you in the general case. Maybe you are doing some bizarre experiment
> with your handwritten assembly version of _start?
>
Yeah...to satisfy your curiosity here's the story.
Long time ago when I did the 128-bit floating point implementation for
s390 I wrote some testing machinery that made sure memcheck propagated
the undefinedness through the new IROps properly. Basically
- load an undefined value into a register
- AND it with a one-hot bit-pattern (leaving a single bit undefined)
- perform the operation we're interested in
- do a conditional branch on the result
- depending on the operation the conditional branch would test
a specific bit or all of them
- check for reported error
I'm trying to revive that machinery because I think it's useful to have,
in particular for testing upcoming dfp support.
So what I'm doing is to generate the smallest possible asm snippets that
implement what I described above.
I ran into trouble with step #1 :)
Cheers,
Florian
|
|
From: Julian S. <js...@ac...> - 2012-03-02 12:17:34
|
On Thursday, February 23, 2012, Florian Krohm wrote: > On 02/23/2012 03:28 AM, Julian Seward wrote: > > So .. apart from the curiosity value, I am not sure what this will buy > > you in the general case. Maybe you are doing some bizarre experiment > > with your handwritten assembly version of _start? > > Yeah...to satisfy your curiosity here's the story. > Long time ago when I did the 128-bit floating point implementation for > s390 I wrote some testing machinery that made sure memcheck propagated > the undefinedness through the new IROps properly. Basically > - load an undefined value into a register > - AND it with a one-hot bit-pattern (leaving a single bit undefined) > - perform the operation we're interested in > - do a conditional branch on the result > - depending on the operation the conditional branch would test > a specific bit or all of them > - check for reported error This all seems an excellent thing to do (I wish we had more of it). However .. wouldn't it be a bit easier by wrapping each insn up in a bit of inline assembly, and feeding it data from some uninitialised block? Then you could do a whole bunch at once rather than creating one executable per insn. Just my Eur 0.02. [Am sure you've already thought of this, btw ..] J |
|
From: Florian K. <br...@ac...> - 2012-02-25 14:57:47
|
On 02/23/2012 03:28 AM, Julian Seward wrote:
>
>> (1) Is the zero-out done on purpose? Perhaps according to the rationale:
>> by the time execution reaches those parts of the application
>> program a user cares about, glibc will have populated the registers
>> with some defined values anyhow? So why bother here?
>
> Well .. so the zero-out for the other architectures .. I did that because
> from a conservatism point of view. Getting an initial state that works
> reliably has been difficult (on ppc64-linux I had mucho trouble) so
> zeroing everything seemed simplest.
>
>> (2) If I wanted to initialize those shadow areas to mark the registers
>> as uninitialized, what bit pattern should be stored? All bits 1?
>
> Yeah, 0 = defined, 1 = undefined.
This is for vex_shadow1.
I think that vex_shadow2 should be initialized with all-zero. From what
I understand it holds otags and since no valid otag can be zero, zero
seems appropriate. Right?
Actually I think that the initialization of the shadow areas should be
done inside the tools. The tools determine ultimately what is stored in
those areas and what the encoded values mean.
Florian
|
|
From: Julian S. <js...@ac...> - 2012-03-02 10:42:22
|
On Saturday, February 25, 2012, Florian Krohm wrote: > On 02/23/2012 03:28 AM, Julian Seward wrote: > >> (1) Is the zero-out done on purpose? Perhaps according to the rationale: > >> by the time execution reaches those parts of the application > >> program a user cares about, glibc will have populated the registers > >> with some defined values anyhow? So why bother here? > > > > Well .. so the zero-out for the other architectures .. I did that because > > from a conservatism point of view. Getting an initial state that works > > reliably has been difficult (on ppc64-linux I had mucho trouble) so > > zeroing everything seemed simplest. > > > >> (2) If I wanted to initialize those shadow areas to mark the registers > >> > >> as uninitialized, what bit pattern should be stored? All bits 1? > > > > Yeah, 0 = defined, 1 = undefined. > > This is for vex_shadow1. > I think that vex_shadow2 should be initialized with all-zero. From what > I understand it holds otags and since no valid otag can be zero, zero > seems appropriate. Right? Yes. > Actually I think that the initialization of the shadow areas should be > done inside the tools. The tools determine ultimately what is stored in > those areas and what the encoded values mean. Yes -- you're exactly right. Zeroing it out is just .. simple. Besides, if any tool uses a shadow value before defining it then there's something funny with the program's startup code (or the the tool). But yes .. from a s/w engineering point of view it's actually more logical for the tools to initialise the shadow state. J |
|
From: Florian K. <br...@ac...> - 2012-03-03 19:13:42
|
On 03/02/2012 05:40 AM, Julian Seward wrote:
> On Saturday, February 25, 2012, Florian Krohm wrote:
>> On 02/23/2012 03:28 AM, Julian Seward wrote:
>>>> (1) Is the zero-out done on purpose? Perhaps according to the rationale:
>>>> by the time execution reaches those parts of the application
>>>> program a user cares about, glibc will have populated the registers
>>>> with some defined values anyhow? So why bother here?
>>>
>>> Well .. so the zero-out for the other architectures .. I did that because
>>> from a conservatism point of view. Getting an initial state that works
>>> reliably has been difficult (on ppc64-linux I had mucho trouble) so
>>> zeroing everything seemed simplest.
>>>
>>>> (2) If I wanted to initialize those shadow areas to mark the registers
>>>>
>>>> as uninitialized, what bit pattern should be stored? All bits 1?
>>>
>>> Yeah, 0 = defined, 1 = undefined.
>>
>> This is for vex_shadow1.
>> I think that vex_shadow2 should be initialized with all-zero. From what
>> I understand it holds otags and since no valid otag can be zero, zero
>> seems appropriate. Right?
>
> Yes.
>
>> Actually I think that the initialization of the shadow areas should be
>> done inside the tools. The tools determine ultimately what is stored in
>> those areas and what the encoded values mean.
>
> Yes -- you're exactly right. Zeroing it out is just .. simple. Besides,
> if any tool uses a shadow value before defining it then there's something
> funny with the program's startup code (or the the tool). But yes .. from
> a s/w engineering point of view it's actually more logical for the tools
> to initialise the shadow state.
>
Actually, at the end of VG_(ii_finalise_image) there is this:
/* Tell the tool that we just wrote to the registers. */
VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
sizeof(VexGuestArchState));
This is good: tells the tool to do the initialization but not how to.
Except that mc_post_reg_write only initialises shadow1 and not shadow2
when running memcheck.
Florian
|