Skip to content

Instantly share code, notes, and snippets.

@yuvalif
Created August 28, 2018 10:48
Show Gist options
  • Select an option

  • Save yuvalif/22c698766cff2961f6c8bcd860e0c511 to your computer and use it in GitHub Desktop.

Select an option

Save yuvalif/22c698766cff2961f6c8bcd860e0c511 to your computer and use it in GitHub Desktop.
forcing race condition on setns() in multi-threaded environment
// This file tries to create net namespace race condition
// in order to verify thread safety of setting a thread to a network namespace.
//
// To compile: g++ -Wall -O2 -o test_ns test_ns.cpp -lpthread
//
// before running, call the following bash command to create network namespaces 0-9:
// for i in {0..9}
// do
// ip netns add ns$i
// done
//
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
void* print_links_in_ns(void* ns_data)
{
// set to ns if given
char* ns = (char*)ns_data;
if (ns_data != nullptr)
{
char fd_name[128];
if (sprintf(fd_name, "/var/run/netns/%s", ns) < 0)
errExit("sprintf");
int fd = open((fd_name), O_RDONLY);
if (fd == -1)
errExit("open");
if (setns(fd, CLONE_NEWNET) == -1)
errExit("setns");
}
// create pipe to read execlp output
int link[2];
char output[4096];
if (pipe(link)==-1)
errExit("fork");
pid_t pid = fork();
if (pid == -1)
{
// failed to fork
errExit("fork");
}
else if (pid > 0)
{
// we are ther parent
// read from pipe connected to child's stdout
close(link[1]);
int nbytes = read(link[0], output, sizeof(output));
// terminate output at newline
char* newline = strchr(output, '\n');
if (newline != nullptr)
*newline = '\0';
if (nbytes < 0)
errExit("read");
if (ns_data == nullptr)
{
// namespace not set in thread
if (nbytes > 1)
printf("Namespace '%s' exists where it shoudln't!!!\n", output);
}
else
{
// namespace is set in thread
if (strcmp(ns, output) != 0)
printf("Wrong namespace '%s', expecting '%s'!!!\n", output, ns);
}
// wait for child to exit
int status;
if (waitpid(pid, &status, 0) != pid)
errExit("waitpid");
free(ns_data);
}
else
{
// we are the child
// write stdout to pipe
dup2(link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
// check ns identity
if (execlp("ip", "ip", "netns", "identify", nullptr) == -1)
errExit("execlp");
}
return nullptr;
}
int main(int argc, char *argv[])
{
const int NUM_OF_THREADS = 400;
pthread_t t[NUM_OF_THREADS];
for (auto i = 0; i < NUM_OF_THREADS; ++i)
{
int ns = rand()%20;
if (ns < 10)
{
// we assume namespaces with names: ns0 - ns9 to exist
char* ns_name = (char*)malloc(8);
if (snprintf(ns_name, 8, "ns%d", ns) < 0)
errExit("sprintf");
printf("spawn thread to query: %s\n", ns_name);
// should belong to ns1
if (pthread_create(&t[i], nullptr, print_links_in_ns, ns_name))
errExit("pthread_create");
}
else
{
printf("spawn thread without namespace\n");
// should not belong to any ns
if (pthread_create(&t[i], nullptr, print_links_in_ns, nullptr))
errExit("pthread_create");
}
}
for (auto i = 0; i < NUM_OF_THREADS; ++i)
{
if(pthread_join(t[i], nullptr))
errExit("pthread_join");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment