Calling main twice behaves differently - linux

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

Related

Why does “date” maps so much memory using “mmap”?

I wrote a simple instrumentation tool (using Intel Pin API)
that traces every call to the mmap system call.
Then I applied the pintool to the "date" command and got the following output.
A few questions please:
1) why does a simple command like "date" maps ~15 MB ?
2) every time mmap is called, the process' page table is updated to
include the newly created mapping - am I correct?
3) using a file descriptor -1 the kernel will 0-initialize the pages.
What is file descriptor 4 used for?
thread begin 0
entry: addr 0 , length 8 KB , fd -1
exit: returned with 0x2b5d7bc08000
entry: addr 0 , length 101 KB , fd 4
exit: returned with 0x2b5d7bc66000
entry: addr 0 , length 2079 KB , fd 4
exit: returned with 0x2b5d7bcb2000
entry: addr 0x2b5d7beb8000 , length 8 KB , fd 4
exit: returned with 0x2b5d7beb8000
entry: addr 0 , length 3833 KB , fd 4
exit: returned with 0x2b5d7bed0000
entry: addr 0x2b5d7c284000 , length 24 KB , fd 4
exit: returned with 0x2b5d7c284000
entry: addr 0x2b5d7c28a000 , length 17 KB , fd -1
exit: returned with 0x2b5d7c28a000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c2b0000
entry: addr 0 , length 2161 KB , fd 4
exit: returned with 0x2b5d7c2ba000
entry: addr 0x2b5d7c4d1000 , length 8 KB , fd 4
exit: returned with 0x2b5d7c4d1000
entry: addr 0x2b5d7c4d3000 , length 13 KB , fd -1
exit: returned with 0x2b5d7c4d3000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c4e7000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c4ee000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c4f3000
entry: addr 0 , length 7051 KB , fd 4
exit: returned with 0x2b5d7c777000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c5ff000
entry: addr 0 , length 4 KB , fd -1
exit: returned with 0x2b5d7c665000
thread end 0 code 0
total mapped 15328 KB
If anyone is interested the pintool code is:
#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
#include <syscall.h>
#include "pin.H"
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "PintoolResults.out", "specify output file name");
// Thread specific data
TLS_KEY mlog_key;
void PrintIndent(std::ostream& os, int indent) {
for (int i=0 ; i<indent ; ++i)
os << " ";
}
// MLOG - thread specific data that is not handled by the buffering API.
class MLOG {
public:
MLOG(THREADID tid)
: indent(3), syscall_num(0), total_mapped(0) {
std::string output_file_name = KnobOutputFile.Value() + "-" + decstr(tid);
out.open(output_file_name.c_str());
if (!out) {
std::cerr << "Error: could not open output file." << std::endl;
}
}
~MLOG() {
out.close();
}
/* Public class members */
std::ofstream out; // where to write the results
int indent;
// 3 integers to save the context before syscalls
UINT64 syscall_num;
UINT64 total_mapped;
};
//==============================================================
// Analysis Routines
//==============================================================
// This routine is executed every time a thread is created.
VOID ThreadStart(THREADID tid, CONTEXT *ctxt, INT32 flags, VOID *v) {
// There is a new MLOG for every thread
MLOG * mlog = new MLOG(tid);
// A thread will need to look up its MLOG, so save pointer in TLS
PIN_SetThreadData(mlog_key, mlog, tid);
mlog->out << "thread begin " << tid << std::endl;
}
// This routine is executed every time a thread is destroyed.
VOID ThreadFini(THREADID tid, const CONTEXT *ctxt, INT32 code, VOID *v) {
MLOG * mlog = static_cast<MLOG*>(PIN_GetThreadData(mlog_key, tid));
mlog->out << "thread end " << tid << " code " << code << std::endl;
mlog->out << "total mapped " << std::dec << (mlog->total_mapped>>10ul) << " KB" << std::endl;
delete mlog;
PIN_SetThreadData(mlog_key, 0, tid);
}
VOID SyscallEntry(THREADID tid, CONTEXT *ctxt, SYSCALL_STANDARD std, VOID *v) {
MLOG * mlog = static_cast<MLOG*>(PIN_GetThreadData(mlog_key, tid));
mlog->syscall_num = PIN_GetSyscallNumber(ctxt, std);
UINT64 syscall_arg0 = PIN_GetSyscallArgument(ctxt, std, 0);
UINT64 syscall_arg1 = PIN_GetSyscallArgument(ctxt, std, 1);
UINT64 syscall_arg4 = PIN_GetSyscallArgument(ctxt, std, 4);
if (mlog->syscall_num == SYS_mmap) {
PrintIndent(mlog->out, mlog->indent);
mlog->out << "entry: addr " << std::hex << std::showbase << syscall_arg0
<< " , length " << std::dec << (syscall_arg1 >> 10ul) << " KB"
<< " , fd " << std::dec << static_cast<int>(syscall_arg4) << "\n";
mlog->indent += 1;
mlog->total_mapped += syscall_arg1;
}
}
VOID SyscallExit(THREADID tid, CONTEXT *ctxt, SYSCALL_STANDARD std, VOID *v) {
MLOG* mlog = static_cast<MLOG*>(PIN_GetThreadData(mlog_key, tid));
UINT64 syscall_num = mlog->syscall_num;
if (syscall_num == SYS_mmap) {
mlog->indent -= 1;
assert(mlog->indent >= 0);
PrintIndent(mlog->out, mlog->indent);
mlog->out << "exit: returned with " << std::hex << std::showbase
<< PIN_GetSyscallReturn(ctxt, std) << "\n";
}
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage() {
PIN_ERROR("This Pintool prints a trace of malloc calls in the guest application\n"
+ KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(INT32 argc, CHAR **argv) {
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
PIN_InitSymbols();
// Register Analysis routines to be called when a thread begins/ends
PIN_AddThreadStartFunction(ThreadStart, 0);
PIN_AddThreadFiniFunction(ThreadFini, 0);
PIN_AddSyscallEntryFunction(SyscallEntry, 0);
PIN_AddSyscallExitFunction(SyscallExit, 0);
// Never returns
PIN_StartProgram();
return 0;
}

How do I compute an RFC 791 IP header checksum?

I am rolling my own IP packets for a teach-yourself-Ruby project, and need to compute the IP header checksum (as described in RFC 791 p.14). One of the related questions that popped up when I typed my question here pointed me at RFC 1071, so I'm probably most of the way there, but just to add to Stack Overflow, can anyone (possibly Future Josh) provide some Ruby code for computing the checksum, assuming the following bit of Ruby:
def build_ip_packet(tos, ttl, protocol, dst_addr, data)
len = (IP_HEADER_LEN * 4) + data.size
ip_header = %w{ #{IP_VERSION} #{IP_HEADER_LEN} #{tos} #{len} #{IP_IDENTIFICATION} #{IP_FLAGS_BIT_0} #{IP_FLAGS_BIT_1} #{IP_FLAGS_BIT_2} #{IP_FRAG_OFFSET} #{ttl} #{protocol} #{hdr_checksum} #{src_addr} #{dst_addr} }
[...]
end
The constants are defined at the top of the file, but they should be self-explanatory if you're looking at RFC791 p.11.
RFC 1071 provides the following sample implementation in C:
in 6
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
register long sum = 0;
while( count > 1 ) {
/* This is the inner loop */
sum += * (unsigned short) addr++;
count -= 2;
}
/* Add left-over byte, if any */
if( count > 0 )
sum += * (unsigned char *) addr;
/* Fold 32-bit sum to 16 bits */
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
checksum = ~sum;
}
I wrote the following C code to unit test it:
#include <stdio.h>
#include <stdlib.h>
unsigned short checksum (int count, unsigned short * addr) {
unsigned long sum = 0;
while (count > 1) {
sum += *addr++;
count -= 2;
}
// Add left-over byte, if any
if (count > 0)
sum += * (unsigned char *) addr;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (unsigned short)sum;
}
void test (const unsigned short expected, const unsigned short got) {
printf(
"%s expected 0x%04x, got 0x%04x\n",
(expected == got ? "OK" : "NOK"),
expected,
got
);
}
int main(void) {
unsigned short *buf = calloc(1024, sizeof(unsigned short));
buf[0] = 0x0000;
test(
0x0,
checksum(2, buf)
);
buf[0] = 0x0001;
buf[1] = 0xf203;
buf[2] = 0xf4f5;
buf[3] = 0xf6f7;
test(
0xddf2,
checksum(8, buf)
);
return 0;
}
It is slightly disconcerting that my code does not take the bitwise NOT of sum in the last line of the checksum() function like the RFC implementation does, but adding the bitwise NOT broke my unit tests.
Running the code yields:
: jglover#jglover-3; /tmp/rfc-1071-checksum
OK expected 0x0000, got 0x0000
OK expected 0xddf2, got 0xddf2
I ported this to Ruby as follows:
def rfc1071_checksum(header_fields)
checksum = 0
header_fields.each{|field| checksum += field}
while (checksum >> 16) != 0
checksum = (checksum & 0xffff) + (checksum >> 16)
end
return checksum
end
I call the method like this:
def build_ip_packet(tos, ttl, protocol, dst_addr, data)
len = (IP_HEADER_LEN * 4) + data.size
# Header checksum is set to 0 for computing the checksum; see RFC 791 p.14
hdr_checksum = 0
src_addr = IPAddr.new('127.0.0.1')
header_fields = [
( ((IP_VERSION << 4) + IP_HEADER_LEN) << 8 ) + ( tos ),
len,
IP_IDENTIFICATION,
( (IP_FLAGS_BIT_0 << 15) + (IP_FLAGS_BIT_1 << 14) + (IP_FLAGS_BIT_2 << 13) + (IP_FRAG_OFFSET << 12)),
( ttl << 8 ) + ( protocol ),
hdr_checksum,
src_addr & 0xff00, # 16 most significant bits
src_addr & 0x00ff, # 16 least significant bits
dst_addr & 0xff00, # 16 most significant bits
dst_addr & 0x00ff, # 16 least significant bits
]
hdr_checksum = rfc1071_checksum(header_fields)
return nil
end
And here are the unit tests:
require 'test/unit'
require 'lib/traceroute'
class TestTraceroute < MiniTest::Unit::TestCase
def test_rfc1071_checksum
[
[ 0x0000, [ 0x0000 ] ],
[
0xddf2,
[
0x0001f203,
0xf4f5f6f7,
]
],
].each{|input_record|
got = Traceroute.new.rfc1071_checksum(input_record[1])
expected = input_record[0]
assert_equal(got, expected, "rfc1071_checksum: #{input_record[1].inspect}")
}
end
end

Why does Python perform an `TIOCGWINSZ` ioctl call when opening a character device file?

I'm currently developing a Linux device driver and am currently putting the whole character device infrastructure business in place; mostly boring stuff, populate file_operations structure with handler functions and in parallel I'm writing a small test suite in Python.
Relevant code kernel side (not much to see here really)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
/* We try to keep the preprocessor #if/#endif mayhem to a minimum, but
* this is one of the few places where there's no way around it, other
* than obfuscating the function definitions behind proprocessor mayhem
* at a different place.
*
* The following two helper macros abstract away the kernel version specific
* ioctl function prototypes and access to the file pointer inode. */
#define IOCTL_FUNC(name, _inode, _filp, _cmd, _args) \
int name(struct inode *_inode, struct file *_filp, unsigned int _cmd, unsigned long _args)
#define IOCTL_INODE(_filp, _inode) \
(void)_inode;
#else
#define IOCTL_FUNC(name, _inode, _filp, _cmd, _args) \
long name(struct file *_filp, unsigned int _cmd, unsigned long _args)
# define IOCTL_INODE(_filp, _inode) \
struct inode *_inode = file_inode(_filp); \
(void)_inode;
#endif
static
int dwdsys_dev_from_inode_or_file(
struct inode *inode,
struct file *filp,
struct dwddev **out_dwd )
{
int rc = -ENODEV;
struct dwddev *dwd = NULL;
struct dwdsys_linux *dsl;
list_for_each_entry( dsl, &dwdsys_list, entry ){
if( MAJOR(asl->devno_base) == MAJOR(inode->i_rdev) ){
unsigned const i_board = MINOR(inode->i_rdev);
if( i_board < dsl->ds.n_boards ){
dwd = dsl->ds.board[i_board];
rc= 0;
break;
}
}
}
/* XXX: cache dwd in either filp->private_data or inode->i_private */
if( !rc ){
if( out_dwd ){ *out_dwd = dwd; }
}
return rc;
}
static IOCTL_FUNC(dwdsys_chrdev_ioctl, inode, filp, cmd, args)
{
int rc;
struct dwddev *dwd= NULL;
IOCTL_INODE(filp, inode);
rc= dwdsys_dev_from_inode_or_file(inode, filp, &dwd);
if( rc ){ goto fail; }
switch( cmd ){
default:
rc= -EINVAL;
break;
case ...:
/* ... */
}
fail:
DWD__TRACE_FUNCTION(dwd, rc, "cmd=0x%08x, args=%p", cmd, (void*)args);
return rc;
}
static struct file_operations dwdsys_chrdev_fops = {
.owner = THIS_MODULE,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
.ioctl = dwdsys_chrdev_ioctl,
#else
.unlocked_ioctl = dwdsys_chrdev_ioctl,
#endif
};
And I just noticed that when I open the driver's device node within the Python interactive REPL the driver will report an unsupported ioctl call, command code 0x5413 which translates into TIOCSWINSZ. That would be ioctl that's being used on VTs/PTYs to set the window size. I can see why the Python REPL would do that ioctl on, say, stdio. But it seems an odd thing to do unconditionally.
Here's what I do in the Python REPL
>>> dwd = open("/dev/dwd0a", "r")
That's it. This will make my driver spit a warning into the kernel log, that an unsupported ioctl was called.
So the question is: Is this intended, specified behaviour? Or is this something unintended, they maybe should be reported as a bug?
Update due to request in comments / console output of strace-ed session
dw#void: ~/dwd/src/linux master ⚡
$ python
Python 3.5.2 (default, Oct 19 2016, 17:19:49)
[GCC 4.9.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> dwd = open("/dev/dwd0a", "r")
[1] + 1906 suspended python
At this point, before hitting enter I suspended the Python REPL process, and attached strace to it, before foregrounding it again…
dw#void: ~/dwd/src/linux master ⚡
$ sudo strace -p 1906 &
[2] 1922
strace: Process 1906 attached
--- stopped by SIGTSTP ---
dw#void: ~/dwd/src/linux master ⚡
$ fg
[1] - 1906 continued python
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=738, si_uid=1000} ---
select(1, [0], NULL, NULL, NULL) = 1 (in [0])
rt_sigaction(SIGWINCH, {0x7fd1524463e0, [], SA_RESTORER|SA_RESTART, 0x7fd152fbcbef}, {0x7fd152668980, [], SA_RESTORER, 0x7fd152fbcbef}, 8) = 0
read(0, "\n", 1) = 1
writev(1, [{iov_base="", iov_len=0}, {iov_base="\n", iov_len=1}], 2) = 1
ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig icanon echo ...}) = 0
rt_sigaction(SIGWINCH, {0x7fd152668980, [], SA_RESTORER, 0x7fd152fbcbef}, {0x7fd1524463e0, [], SA_RESTORER|SA_RESTART, 0x7fd152fbcbef}, 8) = 0
open("/dev/dwd0a", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(240, 0), ...}) = 0
ioctl(3, TIOCGWINSZ, 0x7fffb1291f00) = -1 EINVAL (Invalid argument)
lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Invalid seek)
ioctl(3, TIOCGWINSZ, 0x7fffb1291eb0) = -1 EINVAL (Invalid argument)
getcwd("/home/dw/dwd/src/linux", 1024) = 31
stat("/home/dw/dwd/src/linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.5", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.5/_bootlocale.py", {st_mode=S_IFREG|0644, st_size=1301, ...}) = 0
stat("/usr/lib/python3.5/_bootlocale.py", {st_mode=S_IFREG|0644, st_size=1301, ...}) = 0
open("/usr/lib/python3.5/__pycache__/_bootlocale.cpython-35.pyc", O_RDONLY|O_CLOEXEC) = 4
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
fstat(4, {st_mode=S_IFREG|0644, st_size=1028, ...}) = 0
lseek(4, 0, SEEK_CUR) = 0
fstat(4, {st_mode=S_IFREG|0644, st_size=1028, ...}) = 0
read(4, "\26\r\r\n5\253\7X\25\5\0\0\343\0\0\0\0\0\0\0\0\0\0\0\0\v\0\0\0#\0\0"..., 1029) = 1028
read(4, "", 1) = 0
close(4) = 0
lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Invalid seek)
brk(0x562c9d5c0000) = 0x562c9d5c0000
ioctl(0, TIOCGWINSZ, {ws_row=45, ws_col=115, ws_xpixel=809, ws_ypixel=589}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=45, ws_col=115, ws_xpixel=809, ws_ypixel=589}) = 0
ioctl(0, TIOCGWINSZ, {ws_row=45, ws_col=115, ws_xpixel=809, ws_ypixel=589}) = 0
ioctl(0, TIOCSWINSZ, {ws_row=45, ws_col=115, ws_xpixel=809, ws_ypixel=589}) = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = 0
writev(1, [{iov_base=">>> ", iov_len=4}, {iov_base=NULL, iov_len=0}], 2>>> ) = 4

Reading a file containing characters in parentheses into a vector of integers

I am attempting to read in a file containing characters enclosed in parentheses into a vector of integers.
My text file:
(2 3 4 9 10 14 15 16 17 19)
Heres my code:
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int main(){
ifstream file;
file.open("moves.txt");
vector<int> V;
char c;
if (file){
while (file.get(c)){
if (c != '(' && c != ')' && c != ' ')
V.push_back(c - '0');
}
}
else{
cout << "Error openning file." << endl;
}
for (int i = 0; i < V.size(); i++)
cout << V[i] << endl;
}
My Output:
2
3
4
9
1
0
1
4
1
5
1
6
1
7
1
9
-38
Desired output:
2
3
4
9
10
14
15
16
17
19
What is causing the separation of two digit numbers and why is there a negative number at the end of my output?
Don't read characters one by one : read a line, and parse the numbers within it.
By using the is_number (c++11) function of this answer :
bool is_number(const std::string& s)
{
return !s.empty() && std::find_if(s.begin(),
s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
}
You can read line by line with std::getline and then stream the numbers to a std::stringstream. std::stoi can be used to convert a string to an integer :
std::string line;
while(std::getline(file, line))
{
line.replace(line.begin(), line.begin() + 1, "");
line.replace(line.end() - 2, line.end() - 1, "");
std::string numberStr;
std::stringstream ss(line);
while (ss >> numberStr){
if (is_number(numberStr))
v.push_back(std::stoi(numberStr));
}
}
You'd have to make the replace more robust (by checking the presence of parentheses at these positions)

How to get a “backtrace” (like gdb) using only ptrace (linux, x86/x86_64)

I want to get a backtrace-like output as gdb does. But I want to do this via ptrace() directly. My platform is Linux, x86; and, later x86_64.
Now I want only to read return addresses from the stack, without conversion into symbol names.
So, for test program, compiled in -O0 mode by gcc-4.5:
int g() {
kill(getpid(),SIGALRM);
}
int f() {
int a;
int b;
a = g();
b = a;
return a+b;
}
int e() {
int c;
c = f();
}
main() {
return e();
}
I will start a my program and connect with ptrace to test program at very beginning. Then, I will do PTRACE_CONT and will wait for signal. When test program will do a self-kill; the signal will be delivered to my program. At this moment I want to read return addresses, they will be like (because kill function is active at the moment):
0x00_some_address_in_g
0x00_some_address_in_f
0x00_some_address_in_e
0x00_some_address_in_main
0x00_some_address_in__libc_start_main
How can I find return addresses of currently stopped test process with ptrace? There will be a loop over frames? When should I stop such loop?
PS: yes, this is also very like backtrace(3) libc function in idea, but I want to do this externally via ptrace.
The example posted by osgx will work only with code that uses frame pointers. x86_64 code produced by GCC with optimizations doesn't. The kernel vdso code on x86 doesn't use frame pointers on at least some processors. GCC 4.6 (with optimization) doesn't use frame pointers in x86 mode either.
All of the above combine to make the "stack crawl via frame pointers" exceedingly unreliable.
You can use libunwind (which supports both local (in-process) and global (out-of-process via ptrace) unwinding).
Or you'll have to re-implement very large portion of libunwind.
Example of getting backtrace via ptrace using libunwind.
May be, source of pstack(1) utility will help me: (online git from debian). Unfortunately this is x86 32-bit only
http://anonscm.debian.org/gitweb/?p=collab-maint/pstack.git;a=blob;f=pstack.c;h=61beb8d10fa490492ab351115f261614d00adb6d;hb=HEAD#l547
547 static int crawl(int pid)
548 {
549 unsigned long pc, fp, nextfp, nargs, i, arg;
550 int error_occured = 0;
551
552 errno = 0;
553 fp = -1;
554
555 pc = ptrace(PTRACE_PEEKUSER, pid, EIP * 4, 0);
556 if (pc != -1 || !errno)
557 fp = ptrace(PTRACE_PEEKUSER, pid, EBP * 4, 0);
558
559 if ((pc != -1 && fp != -1) || !errno) {
560 print_pc(pc);
561 for ( ; !errno && fp; ) {
562 nextfp = ptrace(PTRACE_PEEKDATA, pid, fp, 0);
563 if (nextfp == (unsigned) -1 && errno) break;
564
565 nargs = (nextfp - fp - 8) / 4;
566 if (nargs > MAXARGS) nargs = MAXARGS;
567 if (nargs > 0) {
568 fputs(" (", stdout);
569 for (i = 1; i <= nargs; i++) {
570 arg = ptrace(PTRACE_PEEKDATA, pid, fp + 4 * (i + 1), 0);
571 if (arg == (unsigned) -1 && errno) break;
572 printf("%lx", arg);
573 if (i < nargs) fputs(", ", stdout);
574 }
575 fputc(')', stdout);
576 nargs = nextfp - fp - 8 - (4 * nargs);
577 if (!errno && nargs > 0) printf(" + %lx\n", nargs);
578 else fputc('\n', stdout);
579 } else fputc('\n', stdout);
580
581 if (errno || !nextfp) break;
582 pc = ptrace(PTRACE_PEEKDATA, pid, fp + 4, 0);
583 if (pc == (unsigned) -1 && errno) break;
584 fp = nextfp;
585 print_pc(pc);
586 }
587 if (fp) error_occured = 1;
588 } else error_occured = 1;
589
590 if (error_occured) perror("crawl");
591 else errno = 0;
592 return errno;
593 }
594
Also, quick test says that is it not very reliable, but sometimes it can print something.

Resources