1
0

proj/test-memory: Add getline() and getdelim()

This commit is contained in:
2025-05-06 18:36:51 +02:00
parent b29e8fb7d9
commit 2fff206f6a
5 changed files with 129 additions and 27 deletions

View File

@@ -26,7 +26,8 @@ main_intercept: bin/main.o src/intercept.c
--wrap=ftruncate,--wrap=fork,--wrap=wait,--wrap=waitpid,--wrap=pipe,--wrap=dup,--wrap=dup2,--wrap=dup3,\ --wrap=ftruncate,--wrap=fork,--wrap=wait,--wrap=waitpid,--wrap=pipe,--wrap=dup,--wrap=dup2,--wrap=dup3,\
--wrap=execl,--wrap=execlp,--wrap=execle,--wrap=execv,--wrap=execvp,--wrap=execvpe,--wrap=execve,--wrap=fexecve,\ --wrap=execl,--wrap=execlp,--wrap=execle,--wrap=execv,--wrap=execvp,--wrap=execvpe,--wrap=execve,--wrap=fexecve,\
--wrap=socket,--wrap=bind,--wrap=listen,--wrap=accept,--wrap=connect,--wrap=getaddrinfo,--wrap=freeaddrinfo,\ --wrap=socket,--wrap=bind,--wrap=listen,--wrap=accept,--wrap=connect,--wrap=getaddrinfo,--wrap=freeaddrinfo,\
--wrap=send,--wrap=sendto,--wrap=sendmsg,--wrap=recv,--wrap=recvfrom,--wrap=recvmsg --wrap=send,--wrap=sendto,--wrap=sendmsg,--wrap=recv,--wrap=recvfrom,--wrap=recvmsg,\
--wrap=getline,--wrap=getdelim
clean: clean:
rm -rf main_intercept bin/* *.so *.ko *.o rm -rf main_intercept bin/* *.so *.ko *.o

View File

@@ -94,6 +94,8 @@ func_def(ssize_t, sendmsg)(int, const struct msghdr *, int);
func_def(ssize_t, recv)(int, void *, size_t, int); func_def(ssize_t, recv)(int, void *, size_t, int);
func_def(ssize_t, recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *); func_def(ssize_t, recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *);
func_def(ssize_t, recvmsg)(int, struct msghdr *, int); func_def(ssize_t, recvmsg)(int, struct msghdr *, int);
func_def(ssize_t, getline)(char **, size_t *, FILE *);
func_def(ssize_t, getdelim)(char **, size_t *, int, FILE *);
#define func_idx_malloc 0 #define func_idx_malloc 0
#define func_idx_calloc 1 #define func_idx_calloc 1
@@ -151,6 +153,8 @@ func_def(ssize_t, recvmsg)(int, struct msghdr *, int);
#define func_idx_recv 53 #define func_idx_recv 53
#define func_idx_recvfrom 54 #define func_idx_recvfrom 54
#define func_idx_recvmsg 55 #define func_idx_recvmsg 55
#define func_idx_getline 56
#define func_idx_getdelim 57
#define FUNCTIONS \ #define FUNCTIONS \
X(malloc) \ X(malloc) \
@@ -208,7 +212,9 @@ func_def(ssize_t, recvmsg)(int, struct msghdr *, int);
X(sendmsg) \ X(sendmsg) \
X(recv) \ X(recv) \
X(recvfrom) \ X(recvfrom) \
X(recvmsg) X(recvmsg) \
X(getline) \
X(getdelim)
#define case_const(name) case name: return #name #define case_const(name) case name: return #name
@@ -2756,3 +2762,54 @@ ssize_t sym(recvmsg)(int sockfd, struct msghdr *message, int flags) {
} }
return ret; return ret;
} }
ssize_t sym(getline)(char **restrict lineptr, size_t *restrict n, FILE *restrict stream) {
init();
Dl_info info;
if (!dladdr(ret_addr, &info) || !func_flags[func_idx_getline] || !lib_flags[lib_idx]) return __real_getline(lineptr, n, stream);
char src_file[256];
msg("getline(%p:%p, %p:%li, %p)" ret_str, lineptr, lineptr != NULL ? lineptr : NULL, n, n != NULL ? *n : 0, stream, ret_data);
if (mode >= 4) {
char msg_buf[BUFFER_SIZE];
rcv(msg_buf, sizeof(msg_buf));
if (strncmp(msg_buf, "modify ", 7) == 0) {
// TODO getline modify
fprintf(stderr, "intercept: %s: modify command not implemented\n", "getline");
} else if_return_int_errno(getline)
else if_fail_int_errno(getline)
else if_invalid(getline)
}
ssize_t ret = __real_getline(lineptr, n, stream);
if (ret >= 0) {
msg("return %li; errno %s; n=%li, line=%eb", ret, strerrorname_np(errno), *n, ret, *lineptr);
} else {
msg("return %li; errno %s; n=%li", ret, strerrorname_np(errno), *n);
}
return ret;
}
ssize_t sym(getdelim)(char **restrict lineptr, size_t *restrict n, int delim, FILE *restrict stream) {
init();
Dl_info info;
if (!dladdr(ret_addr, &info) || !func_flags[func_idx_getdelim] || !lib_flags[lib_idx]) return __real_getdelim(lineptr, n, delim, stream);
char src_file[256];
msg("getdelim(%p:%p, %p:%li, %p)" ret_str, lineptr, lineptr != NULL ? lineptr : NULL, n, n != NULL ? *n : 0, stream, ret_data);
if (mode >= 4) {
char msg_buf[BUFFER_SIZE];
rcv(msg_buf, sizeof(msg_buf));
if (strncmp(msg_buf, "modify ", 7) == 0) {
// TODO getdelim modify
fprintf(stderr, "intercept: %s: modify command not implemented\n", "getdelim");
} else if_return_int_errno(getdelim)
else if_fail_int_errno(getdelim)
else if_invalid(getdelim)
}
ssize_t ret = __real_getdelim(lineptr, n, delim, stream);
if (ret >= 0) {
msg("return %li; errno %s; n=%li, line=%eb", ret, strerrorname_np(errno), *n, ret, *lineptr);
} else {
msg("return %li; errno %s; n=%li", ret, strerrorname_np(errno), *n);
}
return ret;
}

View File

@@ -1,16 +1,26 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Optional, TypedDict, NotRequired, BinaryIO from typing import Optional, TypedDict, NamedTuple, NotRequired, BinaryIO
from socketserver import UnixStreamServer, StreamRequestHandler, ThreadingMixIn from socketserver import UnixStreamServer, StreamRequestHandler, ThreadingMixIn
import os import os
import re import re
type Pointer = int type Pointer = int
type PointerTo[T] = tuple[Pointer, T]
type Constant = tuple[int, str] class PointerTo[T](NamedTuple):
type Flags = tuple[int, list[str]] ptr: Pointer
target: T
class Constant(NamedTuple):
raw: int
name: str
class Flags(NamedTuple):
bitfield: int
flags: list[str]
StructTimeSpec = TypedDict('StructTimeSpec', {'tv_sec': int, 'tv_nsec': int}) StructTimeSpec = TypedDict('StructTimeSpec', {'tv_sec': int, 'tv_nsec': int})
StructSigAction = TypedDict('StructSigAction', {'sa_flags': Flags, 'sa_handler': NotRequired[Pointer], 'sa_sigaction': NotRequired[Pointer], 'sa_mask': list[str]}) StructSigAction = TypedDict('StructSigAction', {'sa_flags': Flags, 'sa_handler': NotRequired[Pointer], 'sa_sigaction': NotRequired[Pointer], 'sa_mask': list[str]})
StructSockAddr = TypedDict('StructSockAddr', {'sa_family': Constant, 'sa_data': NotRequired[bytes], StructSockAddr = TypedDict('StructSockAddr', {'sa_family': Constant, 'sa_data': NotRequired[bytes],
@@ -166,7 +176,7 @@ class Parser:
if m: if m:
idx = len(m.group(0)) - 1 idx = len(m.group(0)) - 1
s, i = Parser.parse_str(argument[idx:]) s, i = Parser.parse_str(argument[idx:])
idx = i idx += i
if idx < len(argument) and argument[idx] in ',;': if idx < len(argument) and argument[idx] in ',;':
idx += 1 idx += 1
return s, idx return s, idx
@@ -207,7 +217,7 @@ class Parser:
idx += i idx += i
if idx < len(argument) and argument[idx] == ',': if idx < len(argument) and argument[idx] == ',':
idx += 1 idx += 1
return (val, list(l)), idx return PointerTo(val, list(l)), idx
elif argument[idx] == '|': elif argument[idx] == '|':
m = re.match(r'^[| A-Za-z0-9_]*', argument[idx:]) m = re.match(r'^[| A-Za-z0-9_]*', argument[idx:])
flags = m.group(0) flags = m.group(0)
@@ -217,20 +227,20 @@ class Parser:
if idx < len(argument) and argument[idx] == ',': if idx < len(argument) and argument[idx] == ',':
idx += 1 idx += 1
flags = [f.strip() for f in flags[1:-1].split('|') if len(f.strip()) > 0] flags = [f.strip() for f in flags[1:-1].split('|') if len(f.strip()) > 0]
return (val, flags), idx return Flags(val, flags), idx
elif argument[idx] == '"': elif argument[idx] == '"':
s, i = Parser.parse_str(argument[idx:]) s, i = Parser.parse_str(argument[idx:])
idx += i idx += i
if idx < len(argument) and argument[idx] == ',': if idx < len(argument) and argument[idx] == ',':
idx += 1 idx += 1
return (val, s), idx return PointerTo(val, s), idx
elif argument[idx] == '{': elif argument[idx] == '{':
idx += 1 idx += 1
l, i = Parser.parse_args(argument[idx:], named=True) l, i = Parser.parse_args(argument[idx:], named=True)
idx += i idx += i
if idx < len(argument) and argument[idx] == ',': if idx < len(argument) and argument[idx] == ',':
idx += 1 idx += 1
return (val, list(l)), idx return PointerTo(val, l), idx
else: else:
m = re.match(r'[A-Z0-9_]+', argument[idx:]) m = re.match(r'[A-Z0-9_]+', argument[idx:])
if not m: if not m:
@@ -239,7 +249,7 @@ class Parser:
idx += len(value) idx += len(value)
if idx < len(argument) and argument[idx] == ',': if idx < len(argument) and argument[idx] == ',':
idx += 1 idx += 1
return (val, value), idx return Constant(val, value), idx
@staticmethod @staticmethod
def parse_args(arguments: str, named: bool = False, ret: bool = False) -> tuple[tuple or dict, int]: def parse_args(arguments: str, named: bool = False, ret: bool = False) -> tuple[tuple or dict, int]:
@@ -594,12 +604,12 @@ class Parser:
def before_sendto(self, sockfd: int, buf: PointerTo[bytes], length: int, flags: Flags, dest_addr: PointerTo[StructSockAddr], addrlen: int) -> str: def before_sendto(self, sockfd: int, buf: PointerTo[bytes], length: int, flags: Flags, dest_addr: PointerTo[StructSockAddr], addrlen: int) -> str:
raise NotImplementedError() raise NotImplementedError()
def after_sendto(self, sockfd: int, buf: PointerTo[bytes], length: int, flags: Flags, dest_addr: PointerTo[StructSockAddr], addrlen: int, def after_sendto(self, sockfd: int, buf: PointerTo[bytes], length: int, flags: Flags, dest_addr: PointerTo[StructSockAddr], addrlen: int,
ret_value: int, errno: str = None) -> None: ret_value: int, errno: str = None) -> None:
raise NotImplementedError() raise NotImplementedError()
def before_sendmsg(self, sockfd: int, message: StructMsgHdr, flags: Flags) -> str: def before_sendmsg(self, sockfd: int, message: StructMsgHdr, flags: Flags) -> str:
raise NotImplementedError() raise NotImplementedError()
def after_sendmsg(self, sockfd: int, message: StructMsgHdr, flags: Flags, def after_sendmsg(self, sockfd: int, message: StructMsgHdr, flags: Flags,
ret_value: int, errno: str = None) -> None: ret_value: int, errno: str = None) -> None:
raise NotImplementedError() raise NotImplementedError()
def before_recv(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags) -> str: def before_recv(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags) -> str:
raise NotImplementedError() raise NotImplementedError()
@@ -609,12 +619,22 @@ class Parser:
def before_recvfrom(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags, src_addr_ptr: Pointer, addrlen_ptr: Pointer) -> str: def before_recvfrom(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags, src_addr_ptr: Pointer, addrlen_ptr: Pointer) -> str:
raise NotImplementedError() raise NotImplementedError()
def after_recvfrom(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags, src_addr_ptr: Pointer, addrlen_ptr: Pointer, def after_recvfrom(self, sockfd: int, buf_ptr: Pointer, size: int, flags: Flags, src_addr_ptr: Pointer, addrlen_ptr: Pointer,
ret_value: int, errno: str = None, buf: PointerTo[bytes] = None, src_addr: PointerTo[StructSockAddr] = None, addrlen: int = None) -> None: ret_value: int, errno: str = None, buf: PointerTo[bytes] = None, src_addr: PointerTo[StructSockAddr] = None, addrlen: int = None) -> None:
raise NotImplementedError() raise NotImplementedError()
def before_recvmsg(self, sockfd: int, message_ptr: Pointer, flags: Flags) -> str: def before_recvmsg(self, sockfd: int, message_ptr: Pointer, flags: Flags) -> str:
raise NotImplementedError() raise NotImplementedError()
def after_recvmsg(self, sockfd: int, message_ptr: Pointer, flags: Flags, def after_recvmsg(self, sockfd: int, message_ptr: Pointer, flags: Flags,
ret_value: int, errno: str = None, message: PointerTo[StructMsgHdr] = None) -> None: ret_value: int, errno: str = None, message: PointerTo[StructMsgHdr] = None) -> None:
raise NotImplementedError()
def before_getline(self, line_ptr: PointerTo[Pointer], n_ptr: PointerTo[int], stream: Pointer) -> str:
raise NotImplementedError()
def after_getline(self, line_ptr: PointerTo[Pointer], n_ptr: PointerTo[int], stream: Pointer,
ret_value: int, errno: str = None, n: int = None, line: PointerTo[bytes] = None) -> None:
raise NotImplementedError()
def before_getdelim(self, line_ptr: PointerTo[Pointer], n_ptr: PointerTo[int], delim: int, stream: Pointer) -> str:
raise NotImplementedError()
def after_getdelim(self, line_ptr: PointerTo[Pointer], n_ptr: PointerTo[int], delim: int, stream: Pointer,
ret_value: int, errno: str = None, n: int = None, line: PointerTo[bytes] = None) -> None:
raise NotImplementedError() raise NotImplementedError()

View File

@@ -74,25 +74,25 @@ class MemoryAllocationParser(Parser):
self.max_allocated = total self.max_allocated = total
def after_malloc(self, size, ret_value, errno=None) -> None: def after_malloc(self, size, ret_value, errno=None) -> None:
self.num_alloc += 1
if ret_value != 0: if ret_value != 0:
self.num_alloc += 1
self.allocated[ret_value] = (self.get_call_id('malloc'), size) self.allocated[ret_value] = (self.get_call_id('malloc'), size)
self.update_max_allocated() self.update_max_allocated()
def after_calloc(self, nmemb, size, ret_value, errno=None) -> None: def after_calloc(self, nmemb, size, ret_value, errno=None) -> None:
self.num_alloc += 1
if ret_value != 0: if ret_value != 0:
self.num_alloc += 1
self.allocated[ret_value] = (self.get_call_id('calloc'), nmemb * size) self.allocated[ret_value] = (self.get_call_id('calloc'), nmemb * size)
self.update_max_allocated() self.update_max_allocated()
def after_realloc(self, ptr, size, ret_value, errno=None) -> None: def after_realloc(self, ptr, size, ret_value, errno=None) -> None:
if ptr == 0: if ptr == 0:
self.num_alloc += 1
if ret_value != 0: if ret_value != 0:
self.num_alloc += 1
self.allocated[ret_value] = (self.get_call_id('realloc'), size) self.allocated[ret_value] = (self.get_call_id('realloc'), size)
else: else:
self.num_realloc += 1
if ret_value != 0 and ptr in self.allocated: if ret_value != 0 and ptr in self.allocated:
self.num_realloc += 1
v = self.allocated[ptr] v = self.allocated[ptr]
del self.allocated[ptr] del self.allocated[ptr]
self.allocated[ret_value] = (v[0], size) self.allocated[ret_value] = (v[0], size)
@@ -100,22 +100,46 @@ class MemoryAllocationParser(Parser):
def after_reallocarray(self, ptr, nmemb, size, ret_value, errno=None) -> None: def after_reallocarray(self, ptr, nmemb, size, ret_value, errno=None) -> None:
if ptr == 0: if ptr == 0:
self.num_alloc += 1
if ret_value != 0: if ret_value != 0:
self.num_alloc += 1
self.allocated[ret_value] = (self.get_call_id('reallocarray'), nmemb * size) self.allocated[ret_value] = (self.get_call_id('reallocarray'), nmemb * size)
else: else:
self.num_realloc += 1
if ret_value != 0: if ret_value != 0:
self.num_realloc += 1
v = self.allocated[ptr] v = self.allocated[ptr]
del self.allocated[ptr] del self.allocated[ptr]
self.allocated[ret_value] = (v[0], nmemb * size) self.allocated[ret_value] = (v[0], nmemb * size)
self.update_max_allocated() self.update_max_allocated()
def after_getaddrinfo(self, node, service, hints, res_ptr, ret_value, errno=None, res=None) -> None: def after_getaddrinfo(self, node, service, hints, res_ptr, ret_value, errno=None, res=None) -> None:
self.num_alloc += 1 if ret_value.raw == 0 and res is not None:
if ret_value[0] == 0 and res is not None: self.num_alloc += 1
size = sum(48 + r['ai_addrlen'] for r in res[1]) size = sum(48 + r['ai_addrlen'] for r in res.target)
self.allocated[res[0]] = (self.get_call_id('getaddrinfo'), size) self.allocated[res.ptr] = (self.get_call_id('getaddrinfo'), size)
self.update_max_allocated()
def after_getline(self, line_ptr, n_ptr, stream, ret_value, errno=None, n=None, line=None) -> None:
if ret_value >= 0 and n is not None and line is not None:
if line_ptr.target == 0:
self.num_alloc += 1
self.allocated[line.ptr] = (self.get_call_id('getline'), n)
elif line_ptr.target != line.ptr:
self.num_realloc += 1
v = self.allocated[line.ptr]
del self.allocated[line.ptr]
self.allocated[ret_value] = (v[0], n)
self.update_max_allocated()
def after_getdelim(self, line_ptr, n_ptr, delim, stream, ret_value, errno=None, n=None, line=None) -> None:
if ret_value >= 0 and n is not None and line is not None:
if line_ptr.target == 0:
self.num_alloc += 1
self.allocated[line.ptr] = (self.get_call_id('getdelim'), n)
elif line_ptr.target != line.ptr:
self.num_realloc += 1
v = self.allocated[line.ptr]
del self.allocated[line.ptr]
self.allocated[ret_value] = (v[0], n)
self.update_max_allocated() self.update_max_allocated()
def after_free(self, ptr) -> None: def after_free(self, ptr) -> None:

View File

@@ -46,7 +46,7 @@ def main() -> None:
'LD_PRELOAD': os.getcwd() + '/../../intercept/intercept.so', 'LD_PRELOAD': os.getcwd() + '/../../intercept/intercept.so',
'INTERCEPT': 'file:' + log_file, 'INTERCEPT': 'file:' + log_file,
'INTERCEPT_VERBOSE': '1', 'INTERCEPT_VERBOSE': '1',
'INTERCEPT_FUNCTIONS': ','.join(['malloc', 'calloc', 'realloc', 'reallocarray', 'free', 'getaddrinfo', 'freeaddrinfo']), 'INTERCEPT_FUNCTIONS': ','.join(['malloc', 'calloc', 'realloc', 'reallocarray', 'free', 'getaddrinfo', 'freeaddrinfo', 'getline', 'getdelim']),
'INTERCEPT_LIBRARIES': ','.join(['*', '-/lib*', '-/usr/lib*']), 'INTERCEPT_LIBRARIES': ','.join(['*', '-/lib*', '-/usr/lib*']),
}) })
finally: finally: