Raw Clone system call - c

I am trying to use the raw clone system, but I could not find any proper documentation.
I tried to write a small program to try it, but this ends up with a segmentation fault.
I cannot understand where I am wrong.
here is the small application :
define STACK_SIZE 0x10000
define BUFSIZE 200
#define _GNU_SOURCE
void hello (){
fprintf(stderr,"Hello word\n");
_exit(0);
}
int main()
{
int res;
void *stack = mmap(0, STACK_SIZE, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
pid_t ptid, tid;
printf("Stack %p\n", stack + STACK_SIZE);
memset(stack, 0, STACK_SIZE);
res= syscall(SYS_clone,CLONE_SIGHAND|CLONE_FS|CLONE_VM|CLONE_FILES,stack + STACK_SIZE, &tid,&ptid,NULL );
if (!res)
hello();
printf("Clone result %x\n", res);
waitpid(-1, NULL, __WALL);
return 0;
}

The child pops the return address from the empty stack, reading from an unmapped address.
NB: The answer assumes x86_64 ISA, where call places the return address on a stack. Contrast it with AArch64, where the Link Register may hold the return address instead.
Details
According to the man page for clone, "execution in the child continues from the point of the call". Yet, although the child and the parent initially execute the same instructions, they don't share the stack, and a ret instruction behaves differently in the parent and child.
Running the application under GDB reveals that SIGSEGV occurs in the syscall function at ret instruction.
Thread 2 received signal SIGSEGV, Segmentation fault.
[Switching to LWP 415]
syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:40
(gdb) x/i $pc
=> 0x7ffff7ee0745 <syscall+37>: retq
ret pops the return address from the stack. The child, with its empty stack, fails to execute it, while the parent successfully returns to the main function.
You can fix the unmapped access by supplying stack + STACK_SIZE - 8 as the third syscall argument. However, the underlying problem remains: the zero-initialized stack doesn't store the return address. The following image illustrates the problem .
syscall+... means "somewhere in syscall", so as not to clutter the scheme with addresses.
Solution
Macattack's answer already mentioned the wrapper for the clone. Implement the arch-specific wrapper in the vein of the uclibc one.

I can't say I recommend going with clone if you can use pthreads. I've had bad experience with functions such as malloc() in relation to clone.
Have you looked at the man page for documentation?
Here is an example that runs for me. I didn't really examine your code to see why it might be crashing.
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/sched.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
// Allow us to round to page size
#define ROUND_UP_TO_MULTIPLE(a,b) \
( ( (a) % (b) == 0) ? (a) : ( (a) + ( (b) - ( (a) % (b) ) ) ) )
struct argsy {
int threadnum;
};
int fun(void * args) {
struct argsy * arguments = (struct argsy *) args;
fprintf(stderr, "hey!, i'm thread %d\n", arguments->threadnum);
return 0;
}
#define N_THREADS 10
#define PAGESIZE 4096
struct argsy arguments[N_THREADS];
int main() {
assert(PAGESIZE==getpagesize());
const int thread_stack_size = 256*PAGESIZE;
void * base = malloc((((N_THREADS*thread_stack_size+PAGESIZE)/PAGESIZE)*PAGESIZE));
assert(base);
void * stack = (void *)ROUND_UP_TO_MULTIPLE((size_t)(base), PAGESIZE);
int i = 0;
for (i = 0; i < N_THREADS; i++) {
void * args = &arguments[i];
arguments[i].threadnum = i;
clone(&fun, stack+((i+1)*thread_stack_size),
CLONE_FILES | CLONE_VM,
args);
}
sleep(1);
// Wait not implemented
return 0;
}

Related

Callbacks in AIO asynchronous I/O

I have found discussion on using callbacks in AIO asynchronous I/O on the internet. However, what I have found has left me confused. An example code is listed below from a site on Linux AIO. In this code, AIO is being used to read in the contents of a file. My problem is that it seems to me that a code that actually processes the contents of that file must have some point where some kind of block is made to the execution until the read is completed. This code here has no block like that at all. I was expecting to see some kind of call analogous to pthread_mutex_lock in pthread programming. I suppose I could put in a dummy loop after the aio_read() call that would block execution until the read is completed. But that puts me right back to the simplest way of blocking the execution, and then I don't see what is gained by all the coding overhead that goes into establishing a callback. I am obviously missing something. Could someone tell me what it is?
Here is the code. (BTW, the original is in C++; I have adapted it to C.)
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <aio.h>
//#include <bits/stdc++.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
const int BUFSIZE = 1024;
void aio_completion_handler(sigval_t sigval)
{
struct aiocb *req;
req = (struct aiocb *)sigval.sival_ptr; //Pay attention here.
/*Check again if the asynchrony is complete?*/
if (aio_error(req) == 0)
{
int ret = aio_return(req);
printf("ret == %d\n", ret);
printf("%s\n", (char *)req->aio_buf);
}
close(req->aio_fildes);
free((void *)req->aio_buf);
while (1)
{
printf("The callback function is being executed...\n");
sleep(1);
}
}
int main(void)
{
struct aiocb my_aiocb;
int fd = open("file.txt", O_RDONLY);
if (fd < 0)
perror("open");
bzero((char *)&my_aiocb, sizeof(my_aiocb));
my_aiocb.aio_buf = malloc(BUFSIZE);
if (!my_aiocb.aio_buf)
perror("my_aiocb.aio_buf");
my_aiocb.aio_fildes = fd;
my_aiocb.aio_nbytes = BUFSIZE;
my_aiocb.aio_offset = 0;
//Fill in callback information
/*
Using SIGEV_THREAD to request a thread callback function as a notification method
*/
my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
my_aiocb.aio_sigevent.sigev_notify_function = aio_completion_handler;
my_aiocb.aio_sigevent.sigev_notify_attributes = NULL;
/*
The context to be transmitted is loaded into the handler (in this case, a reference to the aiocb request itself).
In this handler, we simply refer to the arrived sigval pointer and use the AIO function to verify that the request has been completed.
*/
my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
int ret = aio_read(&my_aiocb);
if (ret < 0)
perror("aio_read");
/* <---- A real code would process the data read from the file.
* So execution needs to be blocked until it is clear that the
* read is complete. Right here I could put in:
* while (aio_error(%my_aiocb) == EINPROGRESS) {}
* But is there some other way involving a callback?
* If not, what has creating a callback done for me?
*/
//The calling process continues to execute
while (1)
{
printf("The main thread continues to execute...\n");
sleep(1);
}
return 0;
}

Mapping existing memory (data segment) to another memory segment

As the title suggests, I would like to ask if there is any way for me to map the data segment of my executable to another memory so that any changes to the second are updated instantly on the first. One initial thought I had was to use mmap, but unfortunately mmap requires a file descriptor and I do not know of a way to somehow open a file descriptor on my running processes memory. I tried to use shmget/shmat in order to create a shared memory object on the process data segment (&__data_start) but again I failed ( even though that might have been a mistake on my end as I am unfamiliar with the shm API). A similar question I found is this: Linux mapping virtual memory range to existing virtual memory range? , but the replies are not helpful.. Any thoughts are welcome.
Thank you in advance.
Some pseudocode would look like this:
extern char __data_start, _end;
char test = 'A';
int main(int argc, char *argv[]){
size_t size = &_end - &__data_start;
char *mirror = malloc(size);
magic_map(&__data_start, mirror, size); //this is the part I need.
printf("%c\n", test) // prints A
int offset = &test - &__data_start;
*(mirror + offset) = 'B';
printf("%c\n", test) // prints B
free(mirror);
return 0;
}
it appears I managed to solve this. To be honest I don't know if it will cause problems in the future and what side effects this might have, but this is it (If any issues arise I will try to log them here for future references).
Solution:
Basically what I did was use the mmap flags MAP_ANONYMOUS and MAP_FIXED.
MAP_ANONYMOUS: With this flag a file descriptor is no longer required (hence the -1 in the call)
MAP_FIXED: With this flag the addr argument is no longer a hint, but it will put the mapping on the address you specify.
MAP_SHARED: With this you have the shared mapping so that any changes are visible to the original mapping.
I have left in a comment the munmap function. This is because if unmap executes we free the data_segment (pointed to by &__data_start) and as a result the global and static variables are corrupted. When at_exit function is called after main returns the program will crash with a segmentation fault. (Because it tries to double free the data segment)
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define _GNU_SOURCE 1
#include <unistd.h>
#include <sys/mman.h>
extern char __data_start;
extern char _end;
int test = 10;
int main(int argc, char *argv[])
{
size_t size = 4096;
char *shared = mmap(&__data_start, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if(shared == (void *)-1){
printf("Cant mmap\n");
exit(-1);
}
printf("original: %p, shared: %p\n",&__data_start, shared);
size_t offset = (void *)&test - (void *)&__data_start;
*(shared+offset) = 50;
msync(shared, 4096, MS_SYNC);
printf("test: %d :: %d\n", test, *(shared+offset));
test = 25;
printf("test: %d :: %d\n", test, *(shared+offset));
//munmap(shared, 4096);
}
Output:
original: 0x55c4066eb000, shared: 0x55c4066eb000
test: 50 :: 50
test: 25 :: 25

Can ptrace cause the traced process to perform a syscall without access to an executable syscall instruction?

Consider this simple program that just infinitely loops:
int main(void) {
for(;;);
}
It's easy enough to use ptrace to inject a system call into it, like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
struct user_regs_struct regs;
pid_t pid = strtol(argv[1], NULL, 10);
ptrace(PTRACE_ATTACH, pid, 0, 0);
waitid(P_PID, pid, NULL, WSTOPPED);
ptrace(PTRACE_GETREGS, pid, 0, &regs);
if(ptrace(PTRACE_POKETEXT, pid, (void*)regs.rip, (void*)0x050f /* the "syscall" instruction, in little-endian */)) {
perror("PTRACE_POKETEXT");
return 1;
}
regs.rax = SYS_exit;
regs.rdi = 42;
ptrace(PTRACE_SETREGS, pid, 0, &regs);
ptrace(PTRACE_DETACH, pid, 0, 0);
return 0;
}
That will inject the syscall _exit(42); over the infinite loop. It's also possible to do this by looking for an existing syscall instruction instead of just overwriting wherever the instruction pointer happens to be.
Now consider this program, that also (after some setup) just infinitely loops:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
struct mapping_list {
void *start;
size_t len;
struct mapping_list *next;
};
typedef void unmap_all_t(struct mapping_list *list, void *start, size_t len);
extern unmap_all_t unmap_all;
extern const char unmap_all_end[];
__asm__("\n"
"unmap_all:\n"
" movq %rsi, %r8 # save start\n"
" movq %rdi, %r9 # save list\n"
".unmap_list_element:\n"
" movq (%r9), %rdi # pass list->start as addr\n"
" movq 8(%r9), %rsi # pass list->len as length\n"
" movl $11, %eax # SYS_munmap\n"
" syscall\n"
" movq 16(%r9), %r9 # advance to the next list element\n"
" testq %r9, %r9\n"
" jne .unmap_list_element\n"
" movl $11, %eax # SYS_munmap\n"
" movq %r8, %rdi # pass start as addr\n"
" movq %rdx, %rsi # pass len as length\n"
" jmp .final_syscall\n"
" .org unmap_all+4094 # make sure the upcoming syscall instruction is at the very end of the page,\n"
".final_syscall: # given that unmap_all started at the very beginning of it\n"
" syscall\n"
".loop_forever:\n"
" jmp .loop_forever\n"
"unmap_all_end:\n"
);
int main(void) {
FILE *maps = fopen("/proc/self/maps", "r");
if(!maps) {
perror("fopen");
return 1;
}
struct mapping_list *list = NULL;
unsigned long start, end;
char r, w, x;
while(fscanf(maps, "%lx-%lx %c%c%c", &start, &end, &r, &w, &x) == 5) {
while(fgetc(maps) != '\n');
if(x != 'x') continue;
struct mapping_list *new_list = malloc(sizeof(struct mapping_list));
new_list->start = (void*)start;
new_list->len = end - start;
new_list->next = list;
list = new_list;
}
if(fclose(maps)) {
perror("fclose");
return 1;
}
int memfd = syscall(SYS_memfd_create, "unmap_all", 2 /* MFD_ALLOW_SEALING */);
if(memfd == -1) {
perror("memfd_create");
return 1;
}
if(ftruncate(memfd, 8192)) {
perror("ftruncate");
return 1;
}
char *pages = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0);
if(pages == MAP_FAILED) {
perror("mmap");
return 1;
}
memcpy(pages, unmap_all, unmap_all_end - (const char*)unmap_all);
if(munmap(pages, 8192)) {
perror("munmap");
return 1;
}
char *path;
if(asprintf(&path, "/proc/self/fd/%d", memfd) == -1) {
perror("asprintf");
return 1;
}
int memfd_ro = open(path, O_RDONLY);
if(memfd_ro == -1) {
perror("open");
return 1;
}
free(path);
if(fcntl(memfd, 1033 /* F_ADD_SEALS */, 15 /* F_SEAL_SEAL|F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE */)) {
perror("fcntl");
return 1;
}
if(close(memfd)) {
perror("close");
return 1;
}
pages = mmap(NULL, 8192, PROT_READ|PROT_EXEC, MAP_SHARED, memfd_ro, 0);
if(pages == MAP_FAILED) {
perror("mmap");
return 1;
}
if(close(memfd_ro)) {
perror("close");
return 1;
}
((unmap_all_t*)pages)(list, pages, 4096);
__builtin_unreachable();
}
When I try to use my ptrace program on it, the PTRACE_POKETEXT step to write the syscall instruction fails with error EIO, since the containing page is a shared mapping of a read-only file. I also don't have the option to find an existing syscall instruction, as all executable pages except one have been unmapped, and the sole remaining one doesn't contain that instruction anywhere.
Is there any other way to use ptrace to cause that program to execute a syscall, or have I made that completely impossible? (If it matters, assume Linux 4.19 on x86_64.)
The point of the seal is to "prove" that ptrace shouldn't automatically allow writing read-only pages
Seals are related to normal shared memory access between processes.
As I mentioned on your other question, regarding kernel source:
ptrace access via PTRACE_POKETEXT is different. It completely bypasses the protections on a given page. (i.e.) It does not reference anything related to seals.
The poketext operation is handled by completely different code within the kernel and [sort of] just does it via access calls to the VM.
I wouldn't worry about it too much.
You might have a look at CONFIG_HAVE_IOREMAP_PROT
Can ptrace cause the traced process to perform a syscall without access to an executable syscall instruction?
Only if the tracer can generate one using POKETEXT, using current mainline kernels and kernel modules.
Perhaps it is time to reread the first paragraph in man 2 ptrace:
The ptrace() system call provides a means by which one process (the
"tracer") may observe and control the execution of another process
(the "tracee"), and examine and change the tracee's memory and
registers. It is primarily used to implement breakpoint debugging
and system call tracing.
It is a tool for observing an controlling a tracee, not some kind of jail, nor an attacker that a process should protect themselves from.
There are probably other ways a tracee can set up a non-writable executable mapping, unmap all other pages, and ensure nothing in the executable pages left contain a sequence that can be used to construct a system call.
So what? Such cases have not yet popped up in practice, or we'd have modified ptrace facilities to cover that case as well.
If this is a real problem, it seems to me that the best approach would be to add an explicit syscall facility to ptrace. There are a number of options how it could be implemented.
So, any "No" answer to the stated question must be amended with "we can add that feature if needed, though". We don't even need to modify any existing kernels, I think; just write a helper kernel module providing the needed facilities.

Pointer return values in seccomp systemcall trap functions

I'm trying to implement trap functions for different systemcalls.
The target is, that a broker will execute them and then return the result.
So the client won't execute the commands itself.
Seccomp offers the ability to achieve this:
What have i done?
Initialize a signal handler vor SIGSYS signals.
Initialized a seccomp filter with the Action SCMP_ACT_TRAP.
Wait for a SA_SIGINFO signal.
Modify the return value of the resulting system call.
In general, this method works.
It is also used like this in the chromium project and by mozilla.
The Problem
Change the return value for system calls returning integers like open works flawlessly. Changing the return value for functions returning pointers does not work (for example getcwd).
Somehow, just the first parameter is returned and this not even in all cases.
Sometimes NULL is returned.
What i also tried
I also created a working example using ptrace.
The ptrace solution intercepts the system calls by changing the instruction pointer to another user space function and modifying the return call.
That solution works, but is a little bit hacky and not preferred due to the use of ptrace in the background.
Example Code
Here is the minimalistic break down of the code.
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <seccomp.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <sys/socket.h>
#include <dirent.h>
#include <linux/filter.h>
#include <ucontext.h>
extern int errno;
#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
static char fake[100] = "fake";
/*
* Catch violations so we see, which system call caused the problems
*/
static void catchViolation(int sig, siginfo_t* si, void* void_context)
{
int old_errno = errno;
printf("Attempted banned syscall number [%d] see doc/Seccomp.md for more information [%d]\n",
si->si_syscall, sig);
ucontext_t* ctx = (ucontext_t*)void_context;
// Just printing some registers for debugging
printf("RAX IS: %p\n", (void*)SECCOMP_RESULT(ctx));
printf("RIP IS: %p\n", (void*)SECCOMP_IP(ctx));
printf("RDI IS: %p\n", (void*)SECCOMP_PARM1(ctx));
printf("RSI IS: %p\n", (void*)SECCOMP_PARM2(ctx));
printf("RDX IS: %p\n", (void*)SECCOMP_PARM3(ctx));
printf("R10 IS: %p\n", (void*)SECCOMP_PARM4(ctx));
printf("R8 IS: %p\n", (void*)SECCOMP_PARM5(ctx));
printf("R9 IS: %p\n", (void*)SECCOMP_PARM6(ctx));
// Set register 4 to 0 according to ABI and set return value
// to fake address
SECCOMP_PARM4(ctx) = 0;
SECCOMP_RESULT(ctx) = (greg_t)fake;
printf("RAX After Change: %p\n", (void*)SECCOMP_RESULT(ctx));
errno = old_errno;
}
/*
* Setup error handling
*/
static void init_error_handling(){
struct sigaction sa = { .sa_sigaction = catchViolation, .sa_flags = SA_SIGINFO | SA_NODEFER };
if (sigaction(SIGSYS, &sa, NULL)){
printf("Failed to configure SIGSYS handler [%s]\n", strerror(errno));
}
}
void init_seccomp_filters(){
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("Could not start seccomp:");
exit(1);
}
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_TRAP);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvmsg), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lstat), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readlink), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getppid), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
if (seccomp_load(ctx)== -1) {
perror("Could not start seccomp:");
exit(1);
}
}
int main(){
init_error_handling();
init_seccomp_filters();
char dir[100] = "hello";
printf("CALL GETCWD\n");
char *t = getcwd(dir, 100);
printf("---------------------\n");
printf("PTR IS: %p\n", t);
printf("EXPECTED: %p\n", fake);
printf("Text is - %s\n", t);
exit(0);
}
Console output
// SITUATION 1 RETURNING WRONG POINTER
CALL GETCWD
Attempted banned syscall number [79] see doc/Seccomp.md for more information [31]
RAX IS: 0x4f
RIP IS: 0x7f3c1dadff8a
RDI IS: 0x7fff983f8940
RSI IS: 0x64
RDX IS: 0x7f3c1dd9f760
R10 IS: 0x61c
R8 IS: 0x3
R9 IS: 0x410
RAX After Change: 0x563659aa70a0
---------------------
PTR IS: 0x7fff983f8940
EXPECTED: 0x563659aa70a0
Text is - hello
// SITUATION 2 RETURNING NULL
CALL GETCWD
Attempted banned syscall number [79] see doc/Seccomp.md for more information [31]
RAX IS: 0x4f
RIP IS: 0x7eff3372bf8a
RDI IS: 0x7ffce201d880
RSI IS: 0x64
RDX IS: 0x7eff339eb760
R10 IS: 0x61c
R8 IS: 0x3
R9 IS: 0x410
RAX After Change: 0x55fcab2c70a0
---------------------
PTR IS: (nil)
EXPECTED: 0x55fcab2c70a0
Text is - (null)
AFAIK, you properly need this What are the return values of system calls in Assembly?
It's just the Linux syscall conversion, that any return vale from -1 to -4096 are treated as errno.
also, refer to here https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/hppa/syscall.c.html
if ((unsigned long int) __sys_res >= (unsigned long int) -4095)
{
__set_errno (-__sys_res);
__sys_res = -1;
}
return __sys_res;
Also, I curious why is your fake pointer so large?
PS: don't declare errno, it's defined in the system header and usually as macro.
And for getcwd, this Linux syscall doesn't return a pointer, it's defined as this int __getcwd(char* buf, size_t size)

Why does pthread_create() return 12?

For some reason, pthread_create isn't allowing me to pass a struct as an argument. The issue is not system related, although I have not had a chance to test it on anyone else's box. It simply won't allow me to pass a struct for some reason; it returns error #12.
The issue is not with memory. I know 12 is ENOMEM, and "that should be that", but it's not.. it simply won't accept my struct as a pointer.
struct mystruct info;
info.website = website;
info.file = file;
info.type = type;
info.timez = timez;
for(threadid = 0; threadid < thread_c; threadid++)
{
// printf("Creating #%ld..\n", threadid);
retcode = pthread_create(&threads[threadid], NULL, getstuff, (void *) &info);
//void * getstuff(void *threadid);
When I ran this code in GDB, for some reason, it didn't return code 12.. but when I run it from the command line, it returns 12.
Any ideas?
Error code 12 on Linux:
#define ENOMEM 12 /* Out of memory */
You are likely running out of memory. Make sure you're not allocating too many threads, and be sure to pthread_join threads when they're done (or use pthread_detach). Make sure you're not exhausting your memory through other means as well.
Passing a stack object as a parameter to pthread_create is a pretty bad idea, I'd allocate it on the heap. Error 12 is ENOMEM.
Try adding some proper error handling.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static void fail(const char *what, int code)
{
fprintf(stderr, "%s: %s\n", what, strerror(code));
abort();
}
...
if (retcode)
fail("pthread_create", retcode);
On my system, 12 is ENOMEM (out of memory).

Resources