[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#906917: sem_timedwait could always block and returns ETIMEOUT but decrements the value on i686 architecture



Hi,

On 2018-08-22 13:02, Андрей Доценко wrote:
> Package: libc6
> Version: 2.24-11+deb9-u3
> 
> Using sem_timedwait on i686 gives random results. In out proprietary
> software semaphore used by two processes and located in shared memory
> mapped with mmap. All works under amd64 architecture and under another some
> distibutions. But under Debian Stretch amd64 sem_timedwait always blocks
> for timeout and returns ETIMEOUT error. Meanwhile it acquires the lock
> decreasing semaphore value.
> 
> I've tried to make test for this bug. Test reproduces bug only with ASAN
> enabled. Without ASAN enabled it always passes. I've attached test but
> without ASAN support to show that I don't miss anything. I can modify test
> to enable ASAN support but if somebody ask.

Thanks, I have been able to reproduce the problem here, even with glibc
2.29. I have attached a version which doesn't need cmake nor check. 

On your side, have you been able to reproduce the problem *without*
ASAN, even on a bigger codebase? I wonder if it is actually a side
effect of ASAN.

> I've discovered that symbols used by i686 are different from those from
> amd64. On amd64 symbols are:
> ~$ nm "${PROJ_PATH}/Docker/debian/9/amd64/test-bugs/test-bugs"  | grep sem_
>                  U sem_destroy@@GLIBC_2.2.5
>                  U sem_getvalue@@GLIBC_2.2.5
>                  U sem_init@@GLIBC_2.2.5
>                  U sem_post@@GLIBC_2.2.5
>                  U sem_timedwait@@GLIBC_2.2.5
> 00000000004019b0 t test_process_sem_timedwait
> 00000000004011c0 t test_process_sem_timedwait_nolock
> 
> But under i686 symbols are different:
> ~$ nm "${PROJ_PATH}/Docker/debian/9/i386/test-bugs/test-bugs"  | grep sem_
>          U sem_destroy@@GLIBC_2.1
>          U sem_getvalue@@GLIBC_2.1
>          U sem_init@@GLIBC_2.1
>          U sem_post@@GLIBC_2.1
>          U sem_timedwait@@GLIBC_2.2
> 08049f50 t test_process_sem_timedwait
> 08048ee0 t test_process_sem_timedwait_nolock
> 
> As you can see symbols are different for i686. Version of sem_init,
> sem_wait, sem_post, sem_destroy and sem_getvalue is GLIBC_2.1, but version
> of sem_timedwait is GLIBC_2.2.

This is perfectly normal. glibc 2.2.5 is the first glibc version that
supported the amd64 architecture.

> Replacing sem_timedwait with sem_wait makes all work on i686 architecture.
> So sem_wait is ok, but sem_timedwait is not.

I confirm that.

Regards,
Aurelien

-- 
Aurelien Jarno                          GPG: 4096R/1DDD8C9B
aurelien@aurel32.net                 http://www.aurel32.net
/* Build with: gcc -o bug906917 bug906917.c -pthread -fsanitize=address */

#include <assert.h>
#include <errno.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

int main()
{
	sem_t *sem =
	    mmap(NULL, sizeof(*sem), PROT_READ | PROT_WRITE,
		 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	int r;
	r = sem_init(sem, 1, 0);
	if (r == -1) {
		perror("sem_init");
	}
	assert(r == 0);

	pid_t pid = fork();
	if (pid == 0) {
		r = sem_post(sem);
		if (r == -1) {
			exit(1);
		}
		exit(0);
	}
	assert(pid > 0);

	int exit_status;
	pid_t child_pid;
	do {
		child_pid = waitpid(pid, &exit_status, 0);
	}
	while ((child_pid == -1) && (errno == EINTR));
	if (child_pid == -1) {
		perror("waitpid");
	}
	assert(child_pid == pid);
	assert(WIFEXITED(exit_status));
	assert(WEXITSTATUS(exit_status) == 0);

	struct timespec abstime;
	r = clock_gettime(CLOCK_REALTIME, &abstime);
	assert(r == 0);
	abstime.tv_sec += 5;

	int value = -1;
	sem_getvalue(sem, &value);
	assert(value == 1);

	do {
		r = sem_timedwait(sem, &abstime);
	}
	while ((r == -1) && (errno == EINTR));
	if (r == -1) {
		perror("sem_timedwait");
	}
	assert(r == 0);

	value = -1;
	sem_getvalue(sem, &value);
	assert(value == 0);

	sem_destroy(sem);

	return 0;
}

Reply to: