procfs: Support multiple reads of e.g. /proc/*/maps

Refs: #1021
Change-Id: If36e1a0f3f41f0215868daf578e96775d96a59a3
This commit is contained in:
Tomoki Shirasawa
2018-08-22 16:18:23 +09:00
committed by Masamichi Takagi
parent e531ee626e
commit 895a8c4099
10 changed files with 1042 additions and 230 deletions

286
test/issues/1021/C1021.c Normal file
View File

@ -0,0 +1,286 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
int id;
int okcnt;
int ngcnt;
void *area;
void
ok(char *file, char *fmt, ...)
{
va_list ap;
printf("*** C1021T%02d %s ", id, file);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
okcnt++;
}
void
ng(char *file, char *fmt, ...)
{
va_list ap;
printf("*** C1021T%02d %s ", id, file);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
ngcnt++;
}
void
hex(char *bp, int len)
{
unsigned char *buf;
long l;
long p;
long zl = 0;
long zf = 1;
long zp = 0;
for (p = 0; p < len; p += 16) {
int i;
buf = (unsigned char *)bp + p;
l = 16;
if (p + 16 > len) {
l = len - p;
}
if (!zf) {
int zz = 0;
for (i = 0; i < l; i++) {
if (buf[i])
zz = 1;
}
if (l < 16 || !zz) {
zl += 16;
continue;
}
if (zl == 16) {
printf("%016lx 00000000 00000000 00000000 "
"00000000 *................*\n", zp);
}
else if (zl) {
printf(" %08lx - %08lx ZERO\n", zp, p);
}
}
zf = 0;
printf("%08lx ", p);
for (i = 0; i < 16; i++) {
if (i % 4 == 0)
printf(" ");
printf(i < l ? "%02x" : " ", buf[i]);
if (i < l && buf[i])
zf = 1;
}
printf(" *");
for (i = 0; i < 16; i++)
printf(i < l ? "%c" : " ",
isprint(buf[i]) ? buf[i] : '.');
printf("*\n");
zl = 0;
zp = p + 16;
}
}
void
sub(char *file, int mapsflag)
{
int fd;
int fd2;
char buf1[65536];
char buf2[65536];
char buf3[65536];
int n;
int rc;
int pos;
id++;
fd = open(file, O_RDONLY);
if (fd == -1) {
ng(file, "open %s", strerror(errno));
}
else {
ok(file, "open OK");
}
id++;
fd2 = dup(fd);
if (fd2 == -1) {
ng(file, "dup %s", strerror(errno));
}
else {
ok(file, "dup OK");
}
id++;
for (n = 0; (rc = read(fd, buf1 + n, 1)) == 1; n++);
if (rc == -1) {
ng(file, "read(1) %s", strerror(errno));
}
else if (mapsflag && n < 4096) {
ng(file, "read(1) short n=%d", n);
}
else {
ok(file, "read(1) OK n=%d", n);
}
id++;
rc = lseek(fd, 0L, SEEK_SET);
if (rc == -1) {
ng(file, "lseek %s", strerror(errno));
}
else {
ok(file, "lseek OK");
}
if (mapsflag)
munmap(area, 4096);
id++;
pos = 0;
while ((rc = read(fd, buf2 + pos, 1024)) > 0) {
pos += rc;
}
if (rc == -1) {
ng(file, "read(1) %s\n", strerror(errno));
}
else {
if (pos != n) {
ng(file, "read(1024) invalid size %d != %d", pos, n);
}
else if (memcmp(buf1, buf2, n) != 0) {
ng(file, "read(1024) invalid data");
hex(buf1, n);
hex(buf2, n);
}
else {
ok(file, "read(1024) OK");
}
}
id++;
rc = close(fd);
if (rc == -1) {
ng(file, "close %s", strerror(errno));
}
else {
ok(file, "close OK");
}
id++;
rc = read(fd2, buf3, n);
if (rc == -1) {
ng(file, "read(dup) EOF %s", strerror(errno));
}
else if (rc == 0) {
ok(file, "read(dup) EOF OK");
}
else {
ng(file, "read(dup) invalid position");
}
id++;
rc = lseek(fd2, 0L, SEEK_SET);
if (rc == -1) {
ng(file, "lseek(dup) %s", strerror(errno));
}
else {
ok(file, "lseek(dup) OK");
}
id++;
rc = read(fd2, buf3, n);
if (rc == -1) {
ng(file, "read(dup) %s", strerror(errno));
}
else if (rc != n) {
ng(file, "read(dup) too short");
}
else {
rc = read(fd2, buf3 + rc, n);
if (rc == -1) {
ng(file, "read(dup) %s", strerror(errno));
}
else if (rc != 0) {
ng(file, "read(dup) too long");
}
else if (memcmp(buf1, buf3, n) != 0) {
ng(file, "read(dup) invalid data");
hex(buf1, n);
hex(buf3, n);
}
else {
ok(file, "read(dup) OK");
}
}
id++;
rc = close(fd2);
if (rc == -1) {
ng(file, "close(dup) %s", strerror(errno));
}
else {
ok(file, "close(dup) OK");
}
}
int
main(int argc, char **argv)
{
int i;
int pid = getpid();
char file[1024];
for (i = 0; i < 512; i++) {
char *c;
if (i % 2) {
c = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
area = c;
}
else {
c = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
}
if (c == (void *)-1) {
printf("mmap error %d\n", errno);
exit(1);
}
*c = 1;
}
sub("/proc/stat", 0);
sprintf(file, "/proc/%d/auxv", pid);
sub(file, 0);
sprintf(file, "/proc/%d/cmdline", pid);
sub(file, 0);
sprintf(file, "/proc/%d/maps", pid);
sub(file, 1);
sprintf(file, "/proc/%d/status", pid);
sub(file, 0);
sprintf(file, "/proc/%d/task/%d/stat", pid, pid);
sub(file, 0);
if (ngcnt) {
printf("TEST FAILED OK=%d NG=%d\n", okcnt, ngcnt);
exit(1);
}
printf("TEST SUCCESS OK=%d\n", okcnt);
exit(0);
}

81
test/issues/1021/C1021.sh Executable file
View File

@ -0,0 +1,81 @@
#!/bin/sh
BIN=
SBIN=
LTP=
OSTEST=
BOOTPARAM="-c 1-7,17-23,9-15,25-31 -m 10G@0,10G@1"
if ! sudo ls /sys/kernel/debug | grep kmemleak > /dev/null 2>&1; then
echo kmemleak: not found >&2
exit 1
fi
if [ -f ../../../config.h ]; then
str=`grep "^#define BINDIR " ../../../config.h | head -1 | sed 's/^#define BINDIR /BINDIR=/'`
eval $str
fi
if [ "x$BINDIR" = x ];then
BINDIR="$BIN"
fi
if [ -f ../../../Makefile ]; then
str=`grep ^SBINDIR ../../../Makefile | head -1 | sed 's/ //g'`
eval $str
fi
if [ "x$SBINDIR" = x ];then
SBINDIR="$SBIN"
fi
if [ -f $HOME/ltp/testcases/bin/fork01 ]; then
LTPDIR=$HOME/ltp
fi
if [ "x$LTPDIR" = x ]; then
LTPDIR="$LTP"
fi
if [ "x$LTPDIR" != x ]; then
LTPDIR="$LTPDIR/testcases"
fi
if [ -f $HOME/ostest/bin/test_mck ]; then
OSTESTDIR=$HOME/ostest/
fi
if [ "x$OSTESTDIR" = x ]; then
OSTESTDIR="$OSTEST"
fi
if [ ! -x "$OSTESTDIR"/bin/test_mck ]; then
echo no ostest found $OSTEST >&2
exit 1
fi
TESTMCK="$OSTESTDIR/bin/test_mck"
if [ ! -x $SBINDIR/mcstop+release.sh ]; then
echo mcstop+release: not found >&2
exit 1
fi
echo -n "mcstop+release.sh ... "
sudo $SBINDIR/mcstop+release.sh
echo "done"
if [ ! -x $SBINDIR/mcreboot.sh ]; then
echo mcreboot: not found >&2
exit 1
fi
echo -n "mcreboot.sh $BOOTPARAM ... "
sudo $SBINDIR/mcreboot.sh $BOOTPARAM
echo "done"
if [ ! -x $BINDIR/mcexec ]; then
echo no mcexec found >&2
exit 1
fi
################################################################################
sudo sh -c 'echo clear > /sys/kernel/debug/kmemleak'
$BINDIR/mcexec ./C1021
sudo $SBINDIR/mcstop+release.sh
sudo sh -c 'echo scan > /sys/kernel/debug/kmemleak'
if sudo cat /sys/kernel/debug/kmemleak | tee C1021T71.kmemleak | grep 'mcctrl'; then
echo '*** C1021T61 NG (kmemleak)'
else
echo '*** C1021T61 OK (kmemleak)'
fi

View File

@ -0,0 +1,71 @@
Script started on Wed Aug 29 15:21:45 2018
bash-4.2$ make test
sh ./C1021.sh
mcstop+release.sh ... done
mcreboot.sh -c 1-7,17-23,9-15,25-31 -m 10G@0,10G@1 ... done
*** C1021T01 /proc/stat open OK
*** C1021T02 /proc/stat dup OK
*** C1021T03 /proc/stat read(1) OK n=158
*** C1021T04 /proc/stat lseek OK
*** C1021T05 /proc/stat read(1024) OK
*** C1021T06 /proc/stat close OK
*** C1021T07 /proc/stat read(dup) EOF OK
*** C1021T08 /proc/stat lseek(dup) OK
*** C1021T09 /proc/stat read(dup) OK
*** C1021T10 /proc/stat close(dup) OK
*** C1021T11 /proc/12455/auxv open OK
*** C1021T12 /proc/12455/auxv dup OK
*** C1021T13 /proc/12455/auxv read(1) OK n=144
*** C1021T14 /proc/12455/auxv lseek OK
*** C1021T15 /proc/12455/auxv read(1024) OK
*** C1021T16 /proc/12455/auxv close OK
*** C1021T17 /proc/12455/auxv read(dup) EOF OK
*** C1021T18 /proc/12455/auxv lseek(dup) OK
*** C1021T19 /proc/12455/auxv read(dup) OK
*** C1021T20 /proc/12455/auxv close(dup) OK
*** C1021T21 /proc/12455/cmdline open OK
*** C1021T22 /proc/12455/cmdline dup OK
*** C1021T23 /proc/12455/cmdline read(1) OK n=8
*** C1021T24 /proc/12455/cmdline lseek OK
*** C1021T25 /proc/12455/cmdline read(1024) OK
*** C1021T26 /proc/12455/cmdline close OK
*** C1021T27 /proc/12455/cmdline read(dup) EOF OK
*** C1021T28 /proc/12455/cmdline lseek(dup) OK
*** C1021T29 /proc/12455/cmdline read(dup) OK
*** C1021T30 /proc/12455/cmdline close(dup) OK
*** C1021T31 /proc/12455/maps open OK
*** C1021T32 /proc/12455/maps dup OK
*** C1021T33 /proc/12455/maps read(1) OK n=25401
*** C1021T34 /proc/12455/maps lseek OK
*** C1021T35 /proc/12455/maps read(1024) OK
*** C1021T36 /proc/12455/maps close OK
*** C1021T37 /proc/12455/maps read(dup) EOF OK
*** C1021T38 /proc/12455/maps lseek(dup) OK
*** C1021T39 /proc/12455/maps read(dup) OK
*** C1021T40 /proc/12455/maps close(dup) OK
*** C1021T41 /proc/12455/status open OK
*** C1021T42 /proc/12455/status dup OK
*** C1021T43 /proc/12455/status read(1) OK n=255
*** C1021T44 /proc/12455/status lseek OK
*** C1021T45 /proc/12455/status read(1024) OK
*** C1021T46 /proc/12455/status close OK
*** C1021T47 /proc/12455/status read(dup) EOF OK
*** C1021T48 /proc/12455/status lseek(dup) OK
*** C1021T49 /proc/12455/status read(dup) OK
*** C1021T50 /proc/12455/status close(dup) OK
*** C1021T51 /proc/12455/task/12455/stat open OK
*** C1021T52 /proc/12455/task/12455/stat dup OK
*** C1021T53 /proc/12455/task/12455/stat read(1) OK n=92
*** C1021T54 /proc/12455/task/12455/stat lseek OK
*** C1021T55 /proc/12455/task/12455/stat read(1024) OK
*** C1021T56 /proc/12455/task/12455/stat close OK
*** C1021T57 /proc/12455/task/12455/stat read(dup) EOF OK
*** C1021T58 /proc/12455/task/12455/stat lseek(dup) OK
*** C1021T59 /proc/12455/task/12455/stat read(dup) OK
*** C1021T60 /proc/12455/task/12455/stat close(dup) OK
TEST SUCCESS OK=60
*** C1021T61 OK (kmemleak)
bash-4.2$ exit
exit
Script done on Wed Aug 29 15:22:55 2018

13
test/issues/1021/Makefile Normal file
View File

@ -0,0 +1,13 @@
CC=gcc
TARGET=C1021
all:: $(TARGET)
C1021: C1021.c
$(CC) -o C1021 C1021.c -Wall -g
test:: $(TARGET)
sh ./C1021.sh
clean::
rm -f *.o $(TARGET)

58
test/issues/1021/README Normal file
View File

@ -0,0 +1,58 @@
【Issue#1021 動作確認】
□ テスト内容
1. procfs ファイルに対するファイルオペレーションのテスト
Issue#1021 の対応において、procfs の以下のファイル処理を変更している。
/proc/stat
/proc/pid/auxv
/proc/pid/cmdline
/proc/pid/maps
/proc/pid/status
/proc/pid/task/tid/stat
これらのファイルに対するファイルオペレーションとして、以下をテストする。
1) ファイルをopen(2)できること。
2) ファイルディスクリプタをdup(2)できること。
3) 1バイト単位にファイル終端までread(2)できること。
4) lseek(2) できること。(※2)
5) lseek(2) 後に、1024バイト単位に read(2) できること。最初の read(2)と内容が
一致していること。
6) close(2) できること。
7) dup(2) したファイルディスクリプタが EOF になっていること (5 の read(2) の
影響)。
8) dup(2) したファイルディスクリプタを lseek(2) できること。
9) dup(2) したファイルディスクリプタを read(2) し、ファイル全体を 1 回の read(2)
で読み込むことができること。また、最初の read(2) と内容が一致していること。
10) dup(2) したファイルを close(2) できること。
テストケースは以下の通りである。
C1021T01-C1021T10 /proc/stat に対する上記 1) - 10) のテスト
C1021T11-C1021T20 /proc/pid/auxv に対する上記 1) - 10) のテスト
C1021T21-C1021T30 /proc/pid/cmdline に対する上記 1) - 10) のテスト
C1021T31-C1021T40 /proc/pid/maps に対する上記 1) - 10) のテスト (※1, ※2)
C1021T41-C1021T50 /proc/pid/status に対する上記 1) - 10) のテスト
C1021T51-C1021T60 /proc/pid/rask/tid/stat に対する上記 1) - 10) のテスト
※1 /proc/pid/maps はユーザプログラムのメモリの使い方により非常に大きなファイル
になることがあるので、予め mmap を複数回行って4kB以上の read が発生するよう
にしておく。(McKernel 内のバッファが複数ページになる場合のテストを兼ねる)。
※2 /proc/pid/maps の読み込み中に情報が変化しても後続の read(2) に影響しない
ことを確認するため、/proc/pid/maps の lseek(2) 後に munmap(2) を行い、
McKernel の内部情報を変化させる。(後続の read(2) では、munmap 前の情報を
読み込む仕様)。
2. メモリリークが発生していないことの確認
C1021T61 kmemleak を用いて mcctrl の procfs 処理がメモリリークを起こして
いないことを確認する。
□ 実行手順
$ make test
実行できない場合は、C1021.shの以下の行を適切に書き換えた後に実行。
BIN= mcexec が存在するパス
SBIN= mcreboot.sh が存在するパス
LTP= LTP が存在するパス
□ 実行結果
C1021.txt 参照。
全ての項目が OK となっていることを確認。