We should forget about small efficiencies — Donald Knuth
9 Sep
前一陣子上班時, Samuel 跑過來問:
“咦? 你們之前寫某個 Server 的時候, 怎麼讓程式自己產生 Core dump 啊?”
“就是用 setrlimit(2) 的啊!”
“那幫我看一下為啥我照著那樣寫不會動..”
沒想到就開始了殘酷的惡夢~ 讓我們花費了不少工夫才發現為甚麼.
在 Linux 上, 預設是不會有 Core dump 的, 而要讓程式產生 Core dump 的方法就是利用 bash built-in 的 ulimit 指令. 我去年這篇 How to enlarge Coredump Size and File Descriptor Limitations 剛好也有寫到.
不過意外常常有, 所以除了讓 System administrator 設 ulimit -c unlimited 之外, 我自己也會在程式裡面利用 setrlimit(2) 這隻 system call, 讓程式在執行時, 能夠不管 administrator 有沒有用 ulimit 設定 Core dump size, 保證一定會產生 Core dump. 在程式 crash 的時候, Core dump 是很重要線索啊! 就像 CSI 一樣, 讓證據會說話.
可是在看完 Samuel 的程式時, 我和他兩個就覺得很奇怪, 應該是會 work 才對, 因為同樣的 code 寫的程式已經在 production server 上跑了一段時間, 應該不會有問題才對, 這時候, 我才猛然想起來, 管機器的 administrator 曾說過有時候 Core dump 不會出現. “God! 該不會就是同一個問題吧?”
和 Samuel 兩個人找完資料的答案, 就是寫這篇文章的動機了. 我們發現, 如果一個程式按照上面的方法都無法產生 Core dump, 那要看看這個程式是否是用了 setuid(2) 這個 System call. 我們發現, 一個 setuid(2) 或 seteuid(2) 過後的程式, 是沒有辦法產生 Core dump 的.
好巧不巧, 通常 Server 為了一些安全性的考量, 也會實作 setuid(2) 或 seteuid(2) 來達到 Running with Least Privilege (相對應 Windows 的指令就是 Run As). 也就是說, setuid(2) 一定是不能略過的. 解決方法有兩種.
第一種是只有這個程式有效. 用的方法就是 prctl(2). 這個方法是可以改 source, 然後 rebuild 的狀況下才能用. Sample code 如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#define SU_USER "nobody"
int main(void)
{
struct rlimit corelimit;
struct passwd *pw = NULL;
char *cp = NULL;
/* if switch to nobody failed */
if (NULL == (pw = getpwnam(SU_USER)))
{
fprintf(stderr, "Cannot get uid from user(%s). Error: %s\n",
SU_USER, strerror(errno));
return -1;
}
/* try to switch to nobody */
if ((setuid(pw->pw_uid) < 0) || (seteuid(pw->pw_uid) < 0))
{
fprintf(stderr, "Cannot switch to user(%s). Error: %s\n",
SU_USER, strerror(errno));
return -2;
}
/* force to make coredump */
if (prctl(PR_SET_DUMPABLE, 1) < 0)
{
fprintf(stderr, "Cannot enable core dumping. Error: %s\n",
strerror(errno));
return -3;
}
/* set core size to unlimited */
corelimit.rlim_cur = RLIM_INFINITY;
corelimit.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &corelimit) < 0)
{
fprintf(stderr, "Setrlimit failed! Error: %s\n",
strerror(errno));
return -4;
}
/* force to coredump */
*cp = '1';
return 0;
}
這種方法要注意的事情有:
第二種方法是 system-wide 的, 也就是會影響到所有在系統執行的程式. 方法就是 /proc/sys/fs/suid_dumpable. 因為這個和 kernel 的版本有關, 請 man 5 proc 比較保險. 這是在無法動 source code, 而且原程式沒有用 prctl(2) 情況下的殺手鐧.
要注意的事情和第一種方法一樣, 把 /proc/sys/fs/suid_dumpable 設成 1 或 2 的差別, 是會決定 core dump file 的擁有者. 如果設成 1, 就會和第一種方法一樣是被 switch 的 owner (本例是 nobody). 如果設 2 就一定是 root, 但是這個似乎要 kernel 2.6.13 以上才有 support? 我不是很確定.
希望這篇文章給大家當作個參考囉!
PS: 這篇竟然寫了快二個周末, 真是夠久的~
Popularity: 68% [?]
5 Jul
When we write a network server program, I think lots of system calls have their own explicit parameters like socket(), bind(), accept(). But it’s very interesting when we use this system call listen(). Let’s see its prototype:
int listen(int sockfd, int backlog);
Yes, it’s very obvious that the first parameter is the socket fd. But, what’s the meaning of backlog number? Some body would tell us like manpage LISTEN(2) says: “The backlog parameter defines the maximum length the queue of pending connections may grow to. If a connection request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that retries succeed.”
From a robust server’s perspective, what’s the maximum value it should be assign? At first, I assigned very large number like 1,024 (of course, listen system call still returns successfully). After saw the manpage LISTEN(2) in Linux, I was wrong..
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN (128 in Linux 2.0 & 2.2), it is silently truncated to SOMAXCONN.
It doesn’t mention kernel 2.6. But it’s fine. Let’s investigate into Linux kernel source code.
In Linux kernel 2.6.20.1, we can see the listen system call implementation in net/socket.c line 1306. As it shows, the maximum number of backlog cannot be large than sysctl_somaxconn, which is assigned to SOMAXCONN. Furthermore, SOMAXCONN is defined 128 in include/linux/socket.h line 226.
In my opinion, in Linux 2.0 to 2.6, this means backlog cannot exceed 128 by default, or it would be truncated to SOMAXCONN silently like the manpage says.
How about FreeBSD? We can see the note of manpage LISTEN(2) in FreeBSD 6:
The listen() system call appeared in 4.2BSD. The ability to configure the maximum backlog at run-time, and to use a negative backlog to request the maximum allowable value, was introduced in FreeBSD 2.2.
I’m not very familiar with FreeBSD kernel, but let me try to trace. The start point is to check sys/kern/uipc_syscalls.c of cvstag RELENG_6 in FreeBSD. We can see listen system call will invoke solisten(so, uap->backlog, td). Thus, we go to sys/kern/uipc_socket.c now to see the implementation of solisten(struct socket *so, int backlog, struct thread *td). The same, the maximum value is somaxconn which is assigned to SOMAXCONN by default. Finally, we can see the value is defined in sys/sys/socket.h. The value is the same as Linux — 128.
To put it another way, if you’re writing a server program in either Linux or FreeBSD platform, it’s very appropriate to assign the value of backlog to 128. in FreeBSD, however, you can assign a negative backlog to request the maximum allowable value.
You may ask what’s the value of backlog in popular modern server? Let’s check the source of Apache HTTP Server. As you see in /server/listen.c, ap_listenbacklog is assigned to DEFAULT_LISTENBACKLOG which is defined 511 in /include/mpm_common.h.
Popularity: 57% [?]
6 Sep
Linux Per-Process Syscall Hooking, by Pluf.
This document describes a new syscall hooking technique for Linux systems and exposes how it can be implemented as part of a virus or a backdoor in order to take full control over an userland application.
Although there are some well-known methods for hooking functions, they are mostly based on the ELF format itself.
This technique is focused on thoses pieces of code that are externally called by the main program and invoke a system call or system service.
A simple implementation of this hooking mechanism has been developed as a result of the research and it is included with the article.
This code provided does not have all the features you wish but includes the required ones, is not a real backdoor but a simple proof of concept, perfect to write your own one.
Popularity: 30% [?]
5 Sep
Recently, I’m surveying how to deploy Mozilla Firefox/Thunderbird with it’s update channel, but it’s very hard to use for un-official builders like me.
Generally speaking, Firefox will try to send HTTP request to mozilla official site with some client information. For example, a nightly BonEcho will try to get document from this URL.
AS you see, Firefox try to send some information like Firefox version, host architecture, host OS version and so on, and to get server response through SSL (in fact, it’s TLSv1). Of course, you can use any browser to see what the corresponding response is by this URL.
Then Firefox can get type “complete” of patch tag, fetching the .mar file at mozilla official FTP site, and performing update procedure.
Everything seems great, doesn’t it? Not exactly. It means that the Firefox updater program only support .mar file format, not gz, bzip2 format — the common compression we’ve known. Consequently I must have enough knowledge to know how to pack .mar archive as well.
Hence, I create a project called mar hosting on Google Code (Brief introduction on ijliao’s blog). I hope these simple tool s could create, extract, and view the mozilla archive more easy.
Popularity: 21% [?]
5 Jul
用 C 語言窺探記憶體, from 中央大學數學系單維彰的網站.
Introduction to Endianness.
最後我們講一則有趣的故事. 為什麼字元的排序設計, 要叫做大頭或小頭呢? 雖然從前面的解釋, 我們看得出意義, 但是這背後其實有一個故事.
Big-Endian 和 Little-Endian 並不是計算機工程師定的名稱, 而是英文作家 Jonathan Swift 在將近 300 年前創造的名詞!這個名詞出現於 Swift 創作的著名小說 “Gulliver’s Travels“, 中文通常翻譯作《格利佛遊記》或者《大小人國歷險記》或者《小人國歷險記》之類的, 許多讀者大概在童年時期讀過這本書的童話版節譯本.
這部故事書裡, 有一個虛構的『小人國』, 稱為 Lilliput. 格利佛意外抵達 Lilliput 的時候, 該國正在內戰. 內戰分成兩大派系 (沒有派系就沒有內戰): Big-Endian 和 Little-Endian. Big-Endian 和 Little-Endian 為了一件很可笑的小事而分成派系: Big-Endian (保守派) 堅持要從雞蛋比較大的那一頭敲開蛋殼 (大頭開蛋), 而 Little-Endian (改革派) 堅持要從雞蛋比較小的那一頭敲開蛋殼 (小頭開蛋). 雞蛋比較大的那一頭叫做 big-end, 因此支持大頭開蛋者就叫做 big-endian; 同理, 另一派就叫做 little-endian 了.
作者其實可能要藉用這個情節, 來諷刺當時在英國的政治與宗教時事. 後來, 計算機科學家也在爭吵關於 byte order 的問題: 究竟是把高位的字元放在前面比較好, 還是放在後面比較好? 一位當時在美國南加大的計算機科學家 Danny Cohen 在 1980 年 4 月 1 日,發表了標題為 “On Holy Wars and a Plea for Peace” 的文章 (後來在 1981 年刊登於 IEEE 的 Computer 期刊), 把這場計算機科學家的論戰比喻成格利佛在小人國遇見的 Big-Endian 和 Little-Endian 兩派之內戰. 這是非常有趣的譬喻, 一直流傳至今, 成為這兩種硬體設計理念的正式代名詞. 可見, 如果童話書讀得透徹, 長大後可以應用在偉大的論證上.
真奇妙, 現在我才知道 Big-Endian 和 Little-Endian 的由來. ^^
Popularity: 25% [?]
8 Jun
Secure Programming for Linux and Unix HOWTO, from digg.
This book provides a set of design and implementation guidelines for writing secure programs for Linux and Unix systems. If you like Writing Secure Code (Microsoft Press), you maybe like this article as well.
It’s recommended to read Chapter Six: Avoid Buffer Overflow.
Popularity: 18% [?]
1 Sep
SSE Performance Programming, from developer.apple.com.
Here is a tutorial about programming SSE in C language, learning how to do SIMD vector programming for MacOS X for Intel CPU.
Popularity: 19% [?]