-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
valgrind BSD #67153
Comments
This is at least plausibly a valgrind bug? Could you try with a more recent version (I have 3.15 locally, at least)? In any case I suspect that since it works outside of valgrind (I assume, given you didn't mention otherwise) that this would be a bug inside valgrind. |
Yes it does works outside of valgrind, and valgrind is also working for other C program. I don’t know how to get a more recent version valgrind, in the pre compiled package and in the port of FreeBSD the latest version of valgrind is 3.10.1. And when I try to compile valgrind from source it does not work because at some point my cpu is not supported (amd64). If you are also running under FreeBSD could you send me your valgrind binary? |
I am not running under FreeBSD. I suspect this bug is unlikely to make progress as-is; it sounds like it's probably a valgrind bug (missing emulation or lack of support for guard pages, something along those lines). |
Would it be possible to disable guard pages to check if it makes a difference? |
I think no (they're sort of inherently protecting you, i.e., a safety feature). But I'm not sure. You could plausibly comment out the stack guard support in libstd, I guess, and recompile -- https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/stack_overflow.rs |
Guard pages are a prerequisite for ensuring soundness in case stack overflow occurs. C works, because it doesn’t do anything along the lines for you. That being said, there seems to be more stuff going wrong here than just inability to map a page:
Either way, this is more attributable to valgrind rather than Rust. |
I opened a bug with Valgrind. We'll see what they say. |
FreeBSD Valgrind maintainer here. I'm not at all familiar with Rust. Is this code creating a stack for the main process or for a secondary thread? With From what I understand for C/C++ applications, on startup mmap a MAP_GUARD region above the user stack, similar to what this code is doing but much larger and without needing to use mprotect. Valgrind is leaving space for this mapping, and I don't think that it likes client code mmap'ing into it. |
Can anyone comment on my previous question? Is this happening on the main thread during program startup? |
More questions: What is Linux doing differently? Is it also mmap'ing a guard page? And if so, how does it determine the address to use? My impression on FreeBSD is that the rust runtime is getting the user stack via the "kern.usrstack" sysctl. That's what I see with ktrace, but looking at the rust source I also see calls to pthread_attr_getstack. The Valgrind code that allocates the user stack is here starting on line 1048. The FreeBSD code does the same, though obviously there are differences in the I've started doing some tests to see if I can intercept the sysctl for kern.usrstack to return a value that is correct for the guest. |
A few tests on Linux. For a C++ executable, when running standalone the user stack is around 0x7ffeffffff. When running under Valgrind 0x7ffeffff00 is where VG's stack is and the guests stack is around 0x1ffeffffff. With strace I see the following 4k mmaps (I'm assuming the stack guard is 4k). standalone That fits with what I see with the standalone C++ exe. Under Valgrind paulf> strace -ff /home/paulf/tools/valgrind/bin/valgrind -q ./hello 2>&1 | grep mmap | grep 4096 mmap(0x48b6000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x48b6000 This time I don't see an mmap near where the user stack is. |
@paulfloyd I'm not highly familiar with the internals of the Rust compiler, but I can easily answer one of your questions:
|
Fair enough. However, I still don't have answers to many of my questions. What is being done differently on Linux? How do you determine the address for this guard page on FreeBSD? Without this information it is going to be difficult for me to progress. |
Here are the two files that deal with guard pages. |
@paulfloyd I did some work on the guard page stuff this weekend. It's not my area of expertise, but I'm making some progress in fixing stack overflow detection. Fingers crossed, that might fix Valgrind too. |
On 3/29/21 5:23 AM, Alan Somers wrote:
@paulfloyd <https://github.com/paulfloyd> I did some work on the guard
page stuff this weekend. It's not my area of expertise, but I'm making
some progress in fixing stack overflow detection. Fingers crossed,
that might fix Valgrind too.
Based on the rust source links that you sent I wrote the following C++
snippet:
#include <iostream>
#include <pthread.h>
#include <pthread_np.h>
int main()
{
int a;
std::cout << " a addr " << &a << '\n';
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_get_np(pthread_self(), &attr);
void* stackaddr;
size_t stacksize;
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
std::cout << "stack addr " << stackaddr << " stack size " <<
stacksize << '\n';
}
Running it standalone produces
a addr 0x7fffffffe3cc
stack addr 0x7fffdffff000 stack size 536870912
This seems consistent. The address of the local on the stack in main is
close to the address of the stack obtained via pthread_attr_stack.
Running it under Valgrind produces
a addr 0x7fc00045c
stack addr 0x7fffdffff000 stack size 536870912
That's not good! The values returned by pthread_attr_stack haven't
changed, which means that they correspond to the Valgrind host stack
rather than the guest stack.
The pthread_attr_get_np and pthread_attr_getstack functions are just
reading the thread attributes. I need to do more debugging and reading
of the thread and startup code to understand exactly how the stack
address is generated and read. On the Valgrind side, reading these
values needs to be intercepted in some way so that the values are
correct for the guest. I'll also have a go on Linux to see if it is
handling this correctly.
On FreeBSD, pthread_attr_get_np() just copies out the thread's pthread_attr structure which contains the stack address and size.
The FreeBSD libc code that creates the main thread is in /usr/src/lib/libthr/thread/thr_init.c, init_main_thread
``` /*
* Set up the thread stack.
*
* Create a red zone below the main stack. All other stacks
* are constrained to a maximum size by the parameters
* passed to mmap(), but this stack is only limited by
* resource limits, so this stack needs an explicitly mapped
* red zone to protect the thread stack that is just beyond.
*/
if (mmap(_usrstack - _thr_stack_initial -
_thr_guard_default, _thr_guard_default, 0, MAP_ANON,
-1, 0) == MAP_FAILED)
PANIC("Cannot allocate red zone for initial thread");
/*
* Mark the stack as an application supplied stack so that it
* isn't deallocated.
*
* XXX - I'm not sure it would hurt anything to deallocate
* the main thread stack because deallocation doesn't
* actually free() it; it just puts it in the free
* stack queue for later reuse.
*/
thread->attr.stackaddr_attr = _usrstack - _thr_stack_initial;
thread->attr.stacksize_attr = _thr_stack_initial;
thread->attr.guardsize_attr = _thr_guard_default;
thread->attr.flags |= THR_STACK_USER;
```
_usrstack is a global pointer to char
size_t _thr_stack_initial = THR_STACK_INITIAL;
If I understand correctly, _usrstack is set from the kern.usrstack sysctl:
``` if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1)
PANIC("Cannot get kern.usrstack from sysctl")
```
and depending on a couple of env vars _thr_stack_initial comes from getrlimit(RLIMIT_STACK, &rlim)
|
Quick test on Linux - seems OK. paulf> ./sa paulf> valgrind -q ./sa The glibc code to get the pthread attributes is a lot more complicated. There seems to be a general case where it reads thread->stackblock. Possibly this is the case for secondary threads. Anyway, this branch isn't taken for this small example. Indeed, that is what the comment for the else block says. So for the initial thread it reads /proc/self/maps and also uses the internal variable __libc_stack_end. There's a lot of code in VG to handle /proc/self/maps but I'm not familiiar with how it works. |
On 3/29/21 5:23 AM, Alan Somers wrote:
@paulfloyd <https://github.com/paulfloyd> I did some work on the guard
page stuff this weekend. It's not my area of expertise, but I'm making
some progress in fixing stack overflow detection. Fingers crossed,
that might fix Valgrind too.
I may be close to having a solution.
Can you try the attached patch with my git repo?
|
|
Can you confirm whether the above patch works OK? It doesn't break any of the Valgrind amd64 regression tests, and 'rg' and a hello world rust exe both seem to work. |
@paulfloyd I'll probably have time to test it this weekend. |
@paulfloyd Your patch works for me. HOWEVER, even without your patch, valgrind works for me on executables built with the latest Rust compiler ( |
I've integrated my fix to Valgrind as well. |
Now that I see the full version of Paul's fix I understand why #83771 "fixed" the problem. After #83771 , Rustc no longer tries to add a guard page on FreeBSD. However, it still needs to know the address of the stack for the purposes of stack overflow detection. So without Paul's fix in paulfloyd/freebsd_valgrind@5923237 stack overflow detection won't work. @jonas-schievink we can close this issue now. |
Hello,
I’m trying to profile a simple program on FreeBSD 11.2 with rustc 1.39.0 and valgrind 3.10.1.
I always get the following error:
The text was updated successfully, but these errors were encountered: