Skip to content

zend call stack fix freebsd code path. #11766

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

Merged
merged 2 commits into from
Jul 23, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions Zend/zend_call_stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static bool zend_call_stack_is_main_thread(void) {
# endif
}

# ifdef HAVE_PTHREAD_GETATTR_NP
# if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
{
pthread_attr_t attr;
Expand Down Expand Up @@ -145,12 +145,12 @@ static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)

return true;
}
# else /* HAVE_PTHREAD_GETATTR_NP */
# else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
{
return false;
}
# endif /* HAVE_PTHREAD_GETATTR_NP */
# endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */

static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack)
{
Expand Down Expand Up @@ -251,7 +251,7 @@ static bool zend_call_stack_is_main_thread(void)
return is_main == -1 || is_main == 1;
}

# if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GET_STACK)
# if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
{
pthread_attr_t attr;
Expand All @@ -275,6 +275,14 @@ static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
goto fail;
}

error = pthread_attr_getguardsize(&attr, &guard_size);
if (error) {
return false;
}

addr = (char *)addr + guard_size;
max_size -= guard_size;
Copy link
Member

Choose a reason for hiding this comment

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

From reading the code and testing, addr and size reported by pthread_attr_getstack() appear to reflect the usable stack, and they are not affected by the guard size. Based on that, we should not adjust addr and max_size here. But you may have a different experience. Is this required on some FreeBSD version? On Linux this is required in Glibc < 2.8.

In 13.1, the stack is allocated here: https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libthr/thread/thr_stack.c#L276. As we can see, the map size is computed from stacksize + guardsize. stacksize and guardsize come directly from the pthread_attr (they are just rounded to the nearest page multiple). pthread_attr_setstack() does not adjust addr and size with the guardsize, and pthread_attr_setguardsize() does not adjust addr and size either. The get versions of these functions also report these values directly.

I verified with this program: https://gist.github.com/arnaud-lb/a53eb410b7c26b38fd01eac2cee0467e. The program starts 3 threads: Thread 1 with the default attrs, thread 2 with the guard size set to 1MiB, and thread 3 with the guard size set to 16MiB (larger than the stack size). Each thread then recurses until it reaches the address reported by pthread_attr_getstack(). If this address is the actual base of the stack, this should not crash. If this address is in or bellow the guard pages, this should crash. During my tests, this did not crash.

Copy link
Member

Choose a reason for hiding this comment

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

The FreeBSD manpage does not help a lot, but it mentions that these functions conform to POSIX.1. The posix spec about pthread_attr_getstack() says this:

All pages within the stack described by stackaddr and stacksize shall be both readable and writable by the thread.

There could still be a bug or a discrepancy in FreeBSD's implementation, so I wouldn't trust the spec alone, but this matches what I was seeing above.

Copy link
Member Author

Choose a reason for hiding this comment

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

No my mistake even in old releases the guard size is already.

Copy link
Member Author

Choose a reason for hiding this comment

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

No my mistake even in old releases the guard size is already.


stack->base = (int8_t*)addr + max_size;
stack->max_size = max_size;

Expand All @@ -285,12 +293,12 @@ static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
pthread_attr_destroy(&attr);
return false;
}
# else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GET_STACK) */
# else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
{
return false;
}
# endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GET_STACK) */
# endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */

static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack)
{
Expand Down