Issues regarding mutexes on POSIX threads - linux

I'm having some issues with the following code. I just can't seem to find the bug:
1 #include <pthread.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5
6 struct bla
7 {
8 pthread_mutex_t mut;
9 int x;
10 };
11
12
13 pthread_t tid;
14
15 void *thr_fn1 (struct bla *fp);
16
17 int main()
18 {
19 struct bla *fp;
20 fp = (struct bla *) malloc (sizeof(struct bla));
21 fp->x=3;
22 printf ("Initializing mutex_init\n");
23 pthread_mutex_init (&fp->mut,NULL);
24 pthread_create (&tid,NULL,thr_fn1,fp);
25 sleep (2);
26 printf ("Main thread ended sleep. Incrementing.\n");
27 fp->x++;
28 pthread_join (tid,NULL);
29 printf ("x=%d\n",fp->x);
30 pthread_mutex_destroy(&fp->mut);
31 return 0;
32
33 }
34
35 void *thr_fn1 (struct bla *fp)
36 {
37 printf ("Locking new thread!\n");
38 pthread_mutex_lock (&fp->mut);
39 printf ("Sleeping.\n");
40 sleep (5);
41 pthread_mutex_unlock (&fp->mut);
42 printf ("Thread unlocked.\n");
43 return ((void *) 0);
44 }
Why does the value still get incremented at line 27? Shouldn't it be protected by the mutex in the second thread by the lock (line 38)?
Thanks!

There is no automatic association between mutexes and data. If you want a mutex to protect some particular set of data, you are responsible for locking and unlocking the mutex around accesses to that data:
sleep (2);
pthread_mutex_lock(&fp->mut);
printf ("Main thread ended sleep. Incrementing.\n");
fp->x++;
pthread_mutex_unlock(&fp->mut);

Related

Calling main twice behaves differently

I have the following code which is supposed the call the main function twice.
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <limits.h>
#include <link.h>
#include <stdio.h>
#include <time.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
#include <stdlib.h>
#include <sys/mman.h>
extern char** environ;
static int (*main_orig)(
int,
char**,
char**
);
static int (*__libc_start_main_orig)(
int (*)(int, char**, char**),
int,
char**,
int (*)(int, char**, char**),
void (*)(void),
void (*)(void),
void*
);
static int (*init_orig)(
int,
char**,
char**
);
static void (*fini_orig)(void);
static void (*rtld_fini_orig)(void);
static void* stack_end_orig;
static char* libs[] = {
"libpthread-2.22.so",
NULL
};
static void my_dlopen(char* lib_name)
{
int ret;
struct timespec before;
struct timespec after;
long long dl_time;
void* handle = NULL;
(void) clock_gettime(CLOCK_MONOTONIC, &before);
handle = dlopen(lib_name, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
if (handle == NULL) {
fprintf(stdout, "%-35s :: dlopen failed with %s :: ", lib_name, dlerror());
}
else {
fprintf(stdout, "%-35s :: dlopen [OK] :: ", lib_name);
ret = dlclose(handle);
if (ret != 0) {
fprintf(stdout, "dlclose failed with %s :: ", dlerror());
}
else {
fprintf(stdout, "dlclose [OK] :: ");
}
}
(void) clock_gettime(CLOCK_MONOTONIC, &after);
dl_time = ((long long) (after.tv_sec - before.tv_sec)) * 1000000000ll +
((long long) (after.tv_nsec - before.tv_nsec));
fprintf(stdout, "%12lld us\n", dl_time / 1000ll);
}
static int my_main(int argc, char** argv, char** envp)
{
int i;
int ret;
fprintf(stdout, "--- Before main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);
for (i = 0; argv[i] != NULL; i++) {
fprintf(stdout, "argv[%2d] = %s\n", i, argv[i]);
}
for (i = 0; envp[i] != NULL; i++) {
fprintf(stdout, "envp[%2d] = %s\n", i, envp[i]);
}
ret = main_orig(argc, argv, envp);
fprintf(stdout, "--- After main --- %d :: %d :: %p\n", getpid(), getppid(), main_orig);
return ret;
}
int main_hook(int argc, char** argv, char** envp)
{
int i;
int ret;
struct timespec before;
struct timespec after;
long long dl_time;
void* dummy_buffer = NULL;
const size_t dummy_size = 256 * 1024 * 1024;
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
if (ret == -1) {
fprintf(stdout, "mlockall failed with errno = %d\n", errno);
}
else {
fprintf(stdout, "mlockall [OK]\n");
}
ret = mallopt(M_TRIM_THRESHOLD, -1);
if (ret == 1) {
fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [OK]\n");
}
else {
fprintf(stdout, "mallopt(M_TRIM_THRESHOLD, -1) [!!]\n");
}
ret = mallopt(M_MMAP_MAX, 0);
if (ret == 1) {
fprintf(stdout, "mallopt(M_MMAP_MAX, 0) [OK]\n");
}
else {
fprintf(stdout, "mallopt(M_MMAP_MAX, 0) [!!]\n");
}
dummy_buffer = malloc(dummy_size);
if (dummy_buffer != NULL) {
fprintf(stdout, "dummy_buffer = %p :: %zu\n", dummy_buffer, dummy_size);
memset(dummy_buffer, 0x00, dummy_size);
free(dummy_buffer);
}
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
(void) clock_gettime(CLOCK_MONOTONIC, &before);
for (i = 0; libs[i] != NULL; i++) {
my_dlopen(libs[i]);
}
(void) clock_gettime(CLOCK_MONOTONIC, &after);
dl_time = ((long long) (after.tv_sec - before.tv_sec)) * 1000000000ll +
((long long) (after.tv_nsec - before.tv_nsec));
fprintf(stdout, "****************************************************\n");
fprintf(stdout, "Total dlopen time = %lld ms\n", dl_time / 1000000ll);
asm volatile("": : :"memory");
fprintf(stdout, "****************************************************\n");
ret = 0;
ret += my_main(argc, argv, envp);
ret += my_main(argc, argv, envp);
return ret;
}
int __libc_start_main( int (*main)(int, char**, char**),
int argc,
char** argv,
int (*init)(int, char**, char**),
void (*fini)(void),
void (*rtld_fini)(void),
void* stack_end)
{
int i;
int ret;
for (i = 0; environ[i] != NULL; i++) {
char* substr = strstr(environ[i], "LD_PRELOAD=");
if (substr != NULL) {
fprintf(stdout, "%s found and replaced with ", environ[i]);
memset(&environ[i][0], 'x', strlen(environ[i]));
}
fprintf(stdout, "%s\n", environ[i]);
}
__libc_start_main_orig = dlsym(RTLD_NEXT, "__libc_start_main");
main_orig = main;
init_orig = init;
fini_orig = fini;
rtld_fini_orig = rtld_fini;
stack_end_orig = stack_end;
ret = __libc_start_main_orig(main_hook, argc, argv, init, fini, rtld_fini, stack_end);
return ret;
}
This code is compiled as shared library with:
$CC -ggdb -fPIC -shared replace_main.c -o replace_main -ldl -lrt -pthread.
I use the so file like this:
LD_PRELOAD=/var/log/replace_main ls -l /
which gives me:
LD_PRELOAD=/var/log/replace_main found and replaced with xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TERM=xterm-256color
SHELL=/bin/sh
SSH_CLIENT=xxxxxxxxx
OLDPWD=/var/log
SSH_TTY=/dev/pts/0
USER=root
LD_LIBRARY_PATH=xxxxxxxxx
MAIL=/var/mail/root
PATH=xxxxxxxxx
LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
PWD=/var/log
EDITOR=vi
TZ=UTC
PS1=\u#\h:\w\$
SHLVL=1
HOME=/root
LOGNAME=root
SSH_CONNECTION=xxxxxxxxx
_=/bin/ls
****************************************************
mlockall [OK]
mallopt(M_TRIM_THRESHOLD, -1) [OK]
mallopt(M_MMAP_MAX, 0) [OK]
dummy_buffer = 0x5a4020 :: 268435456
****************************************************
libpthread-2.22.so :: dlopen [OK] :: dlclose [OK] :: 596 us
****************************************************
Total dlopen time = 0 ms
****************************************************
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u#\h:\w\$
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
total 19
drwxr-xr-x 2 root root 6176 Jul 17 2018 bin
drwxr-xr-x 2 root root 3 Jul 17 2018 boot
-rw-rw-r-- 1 sirpa tracing 2447 Jul 17 2018 cxp9025851_3.xml
drwxr-xr-x 8 root root 8300 Apr 24 11:48 dev
drwxr-xr-x 38 root root 1584 Jul 17 2018 etc
drwxr-xr-x 3 root root 28 Jul 17 2018 home
drwxr-xr-x 8 root root 5263 Jul 17 2018 lib
-rwxr-xr-x 1 root root 509 Apr 25 2018 linuxrc.sh
drwxr-xr-x 2 root root 3 Apr 22 2018 media
drwxr-xr-x 2 root root 3 Apr 22 2018 mnt
drwxr-xr-x 8 root root 115 Jul 17 2018 opt
dr-xr-xr-x 759 root root 0 Jan 1 1970 proc
drwxrwxrwx 28 sirpa users 4096 Apr 23 17:51 rcs
drwxr-xr-x 2 root root 3 Apr 22 2018 root
drwxrwxrwt 12 root root 540 Apr 24 11:49 run
drwxr-xr-x 2 root root 2868 Jul 17 2018 sbin
drwxr-xr-x 53 root root 12288 Apr 24 10:39 software
dr-xr-xr-x 12 root root 0 Apr 24 11:48 sys
drwxrwxrwt 10 root root 740 Apr 24 12:49 tmp
drwxr-xr-x 10 root root 150 Apr 22 2018 usr
drwxr-xr-x 13 root root 186 Jul 17 2018 var
--- After main --- 25870 :: 3265 :: 0x12068
--- Before main --- 25870 :: 3265 :: 0x12068
argv[ 0] = ls
argv[ 1] = -l
argv[ 2] = /
envp[ 0] = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
envp[ 1] = TERM=xterm-256color
envp[ 2] = SHELL=/bin/sh
envp[ 3] = SSH_CLIENT=xxxxxxxxx
envp[ 4] = OLDPWD=/var/log
envp[ 5] = SSH_TTY=/dev/pts/0
envp[ 6] = USER=root
envp[ 7] = LD_LIBRARY_PATH=xxxxxxxxx
envp[ 8] = MAIL=/var/mail/root
envp[ 9] = PATH=xxxxxxxxx
envp[10] = LTTNG_UST_CLOCK_PLUGIN=xxxxxxxxx
envp[11] = LTTNG_UST_WITHOUT_BADDR_STATEDUMP=xxxxxxxxx
envp[12] = PWD=/var/log
envp[13] = EDITOR=vi
envp[14] = TZ=UTC
envp[15] = PS1=\u#\h:\w\$
envp[16] = SHLVL=1
envp[17] = HOME=/root
envp[18] = LOGNAME=root
envp[19] = SSH_CONNECTION=xxxxxxxxx
envp[20] = _=/bin/ls
bin boot cxp9025851_3.xml dev etc home lib linuxrc.sh media mnt opt proc rcs root run sbin software sys tmp usr var
--- After main --- 25870 :: 3265 :: 0x12068
root#du1:/var/log#
As you can see the ls's main is called twice but the second time it seems that the -l argument is ignored. What can the cause?
PS: I am running the code on ARM Linux variant with kernel 4.1
Thanks,
What can the cause?
The option parsing library has internal state, and you are not resetting that state.
From man 3 getopt:
extern int optind, ...
The variable optind is the index of the next element to be processed in argv.
The system initializes this value to 1. The caller can reset it to 1 to restart
scanning of the same argv, or when scanning a new argument vector.
Since you are not resetting optind inside /bin/ls to 1, when the real main calls getopt, it immediately gets a "no more options" answer.
This can be demonstrated with a trivial program:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int rc;
while ((rc = getopt(argc, argv, "abc")) != -1) {
printf("getopt: '%c'\n", rc);
}
return 0;
}
gcc main.c -o main
LD_PRELOAD=./replace_main.so ./main -ab
...
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a
--- Before main --- 34822 :: 14915 :: 0x55ce59e2768a
argv[ 0] = ./main
argv[ 1] = -ab
--- After main --- 34822 :: 14915 :: 0x55ce59e2768a
Note that getopt lines are missing from the second invocation (just as one would expect).
If I add optind = 1; before the while loop in main.c, then it starts working as you expect:
--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca
--- Before main --- 35487 :: 14915 :: 0x55939fb706ca
argv[ 0] = ./main
argv[ 1] = -ab
getopt: 'a'
getopt: 'b'
--- After main --- 35487 :: 14915 :: 0x55939fb706ca

syscall lseek() is slower in ubuntu 2.6.32 vs 2.6.24

I am running the following code on ubuntu 14 and 18. It is 6X slower on 18 on same hardware. Is there something I am doing wrong?
main(int argc, char *argv[])
{
int fd;
off_t m;
time_t start, ed;
int i, k;
if (argc<2) exit(0);
fd = open(argv[1],O_RDWR|O_CREAT);
if (fd<0) {
printf("cannot open file %s\n", argv[1]);
exit(0);
}
start = time(0L);
for(k=0; k<100; ++k) {
for(i=0;i<500000;++i) {
m = lseek(fd, 0, 0);
if (m== -1) {
printf("lseek failed\n");
exit(0);
}
}
}
ed = time(0L);
printf("Time: %ld\n",ed-start);
}
On ubuntu 14 this takes 4 seconds
On ubuntu 18 it takes 24 seconds
Hardware is same
zx485 was right. the Spectre prevention in kernel was slowing it down by a factor of 6.
Disabling the protection with the following on redhat 7 changed it to normal.
# echo 0 > /sys/kernel/debug/x86/pti_enabled
# echo 0 > /sys/kernel/debug/x86/retp_enabled
# echo 0 > /sys/kernel/debug/x86/ibrs_enabled

Linux Kernel : Not able to load simple linux kernel module with workqueues

I'm having an issue with using the workqueues in my linux kernel module.
My modules compiles without giving any error but at the time of loading it fails.
I'm not able to load the following module and getting following error in dmesg.
[root#nanderson test_mod]# insmod workqueue_test.ko
insmod: ERROR: could not insert module workqueue_test.ko: Unknown symbol in module
[root#nanderson test_mod]# dmesg -c
[50404.453417] workqueue_test: Unknown symbol destroy_workqueue (err 0)
[50404.453437] workqueue_test: Unknown symbol __alloc_workqueue_key (err 0)
[root#nanderson test_mod]#
Following is the module code :-
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/kthread.h>
4 #include <linux/blkdev.h>
5 #include <linux/fs.h>
6 #include <linux/delay.h>
7 #include <linux/workqueue.h>
8 #include <linux/completion.h>
9
10
11 #define LOG_ENTRY() \
12 do {\
13 printk(KERN_INFO "++ %s %d %s\n", __func__, __LINE__,\
14 current->comm);\
15 } while (0);
16
17 #define LOG_INFO() \
18 do {\
19 printk(KERN_INFO "%s %d %s\n", __func__, __LINE__,\
20 current->comm); mdelay(1000);\
21 } while (0);
22
23 #define LOG_EXIT() \
24 do {\
25 printk(KERN_INFO "-- %s %d %s\n", __func__, __LINE__,\
26 current->comm);\
27 } while (0);
28
29
30 void
31 async_callback(void *data)
32 {
33
34 }
35
36 int
37 init_module(void)
38 {
39 struct workqueue_struct *async_queue;
40
41 LOG_ENTRY();
42 if ((async_queue = create_workqueue("HGST_WORKQUEUE")) == NULL) {
43 printk(KERN_ERR "failed to create workqueue\n");
44 return -1;
45 }
46
47 mdelay(10000);
48 destroy_workqueue(async_queue);
49 LOG_EXIT();
50 return 0;
51 }
52
53
54 void
55 cleanup_module(void)
56 {
57 printk(KERN_INFO "Unloading MOdule..\n");
58 }
I also looked into /proc/kallsysm for the unknown symbols reposted by insmod looks like symbols are available following is the output:-
[root#nanderson test_mod]# cat /proc/kallsyms | grep __alloc_workqueue_key
ffffffff81084a10 T __alloc_workqueue_key
ffffffff8187a090 r __ksymtab___alloc_workqueue_key
ffffffff8188bd70 r __kcrctab___alloc_workqueue_key
ffffffff81892ba0 r __kstrtab___alloc_workqueue_key
[root#nanderson test_mod]#
Can someone tell me what might be the problem or I'm missing something ?
Thanks.
You need
MODULE_LICENSE("GPL");
in you code for use GPL symbols (exported using EXPORT_SYMBOL_GPL).
Otherwise module loader simply does not see such symbols.

segfault on write() with ~8MB buffer (OSX, Linux)

I was curious what kind of buffer sizes write() and read() could handle on Linux/OSX/FreeBSD, so I started playing around with dumb programs like the following:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
int main( void ) {
size_t s = 8*1024*1024 - 16*1024;
while( 1 ) {
s += 1024;
int f = open( "test.txt", O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR );
char mem[s];
size_t written = write( f, &mem[0], s );
close( f );
printf( "(%ld) %lu\n", sizeof(size_t), written );
}
return 0;
}
This allowed me to test how close to a seeming "8MB barrier" I could get before segfaulting. Somewhere around the 8MB mark, my program dies, here's an example output:
(8) 8373248
(8) 8374272
(8) 8375296
(8) 8376320
(8) 8377344
(8) 8378368
(8) 8379392
(8) 8380416
(8) 8381440
(8) 8382464
Segmentation fault: 11
This is the same on OSX and Linux, however my FreeBSD VM is not only much faster at running this test, it also can go on for quite a ways! I've successfully tested it up to 511MB, which is just a ridiculous amount of data to write in one call.
What is it that makes the write() call segfault, and how can I figure out the maximum amount that I can possibly write() in a single call, without doing something ridiculous like I'm doing right now?
(Note, all three operating systems are 64-bit, OSX 10.7.3, Ubuntu 11.10, FreeBSD 9.0)
The fault isn't within write(), it's a stack overflow. Try this:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
int main( void )
{
void *mem;
size_t s = 512*1024*1024 - 16*1024;
while( 1 )
{
s += 1024;
int f = open( "test.txt", O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR );
mem = malloc(s);
size_t written = write( f, mem, s );
free(mem);
close( f );
printf( "(%ld) %lu\n", sizeof(size_t), written );
}
return 0;
}

remote debugging gdb multiple process

I am unable to debug a child process of a remote debugging session. I found a similar question How to debug the entry-point of fork-exec process in GDB?
I am following the same procedure, although for a remote target. Is follow-fork-mode child supported for remote targets ?
Following is my sample code..
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5
6 int spawn (void)
7 {
8 pid_t child_pid;
9 /* Duplicate this process. */
10 child_pid = fork ();
11 if (child_pid != 0)
12 /* This is the parent process. */
13 return child_pid;
14 else {
15 /* Now execute PROGRAM, searching for it in the path. */
16 while(1)
17 {
18 static int i = 0;
19 if(i==0) /* break here for child */
20 {
21 printf("I am child\n");
22 }
23 else if(i==3)
24 {
25 return 1;
26 }
27 else
28 {
29 i = 0/0;
30 }
31 i++;
32 }
33 }
34 return 0;
35 }
36 int main ()
37 {
38 /* Spawn a child process running the .ls. command. Ignore the
39 returned child process ID. */
40 printf("Hello World..!!\n");
41 spawn ();
42 printf ("Bbye World..!!\n");
43 return 0;
44 }
Running it with gdb, I can set set break point in child.. all fine here.!!
sh-3.2# gdb fork
(gdb) set follow-fork-mode child
(gdb) set detach-on-fork off
(gdb) b 19
Breakpoint 1 at 0x80483d0: file fork-exec.c, line 19.
(gdb) c
The program is not being run.
(gdb) start
Breakpoint 2 at 0x8048437: file fork-exec.c, line 40.
Starting program: fork
main () at fork-exec.c:40
40 printf("Hello World..!!\n");
(gdb) c
Continuing.
Hello World..!!
[Switching to process 10649]
Breakpoint 1, spawn () at fork-exec.c:19
19 if(i==0) /* break here for child */
(gdb)
However if I try to catch child via gdbserver break point is lost..
sh-3.2# gdbserver :1234 fork &
[5] 10686
sh-3.2# Process fork created; pid = 10689
Listening on port 1234
Run as target remote
sh-3.2# gdb fork
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
Remote debugging from host 127.0.0.1
[New Thread 10689]
0x00bd2810 in _start () from /lib/ld-linux.so.2
(gdb) break 19
Breakpoint 1 at 0x80483d0: file fork-exec.c, line 19.
(gdb) c
Continuing.
Hello World..!!
Bbye World..!!
Child exited with retcode = 0
Program exited normally.
Child exited with status 0
GDBserver exiting
What is the procedure to debug child process in embedded world. I know I can do a process attach, but I want to debug from the very beginning of the child process..
It is called follow-fork. No, it is not supported in gdbserver.
As a (dirty!) workaround, you can just add a sleep() call right after the fork() with a delay long enough for you to get the child PID, attach it with another instance of gdbserver and connect to it with gdb.
It should work with a modern gdb version, according to this bug-report.

Resources