Skip to content

Commit

Permalink
Merge pull request #2387 from cgull/cgull/pty-init-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt authored May 15, 2024
2 parents 61888e0 + 7cc11f0 commit cee7abd
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Open the project in Xcode, open iSH.xcconfig, and change `ROOT_BUNDLE_IDENTIFIER

To set up your environment, cd to the project and run `meson build` to create a build directory in `build`. Then cd to the build directory and run `ninja`.

To set up a self-contained Alpine linux filesystem, download the Alpine minirootfs tarball for i386 from the [Alpine website](https://alpinelinux.org/downloads/) and run `./tools/fakefsify`, with the minirootfs tarball as the first argument and the name of the output directory as the second argument. Then you can run things inside the Alpine filesystem with `./ish -f alpine /bin/login -f root`, assuming the output directory is called `alpine`. If `tools/fakefsify` doesn't exist for you in your build directory, that might be because it couldn't find libarchive on your system (see above for ways to install it.)
To set up a self-contained Alpine linux filesystem, download the Alpine minirootfs tarball for i386 from the [Alpine website](https://alpinelinux.org/downloads/) and run `./tools/fakefsify`, with the minirootfs tarball as the first argument and the name of the output directory as the second argument. Then you can run things inside the Alpine filesystem with `./ish -f alpine /bin/sh`, assuming the output directory is called `alpine`. If `tools/fakefsify` doesn't exist for you in your build directory, that might be because it couldn't find libarchive on your system (see above for ways to install it.)

You can replace `ish` with `tools/ptraceomatic` to run the program in a real process and single step and compare the registers at each step. I use it for debugging. Requires 64-bit Linux 4.11 or later.

Expand Down
4 changes: 2 additions & 2 deletions README_KO.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Xcode로 프로젝트를 열고, iSH.xcconfig 연 후에 `ROOT_BUNDLE_IDENTIFIER

환경을 세팅하기 위해서는 프로젝트 디렉토리로 이동하고 `meson build`를 커맨드 라인에 입력하세요. 그 후 빌드 된 디렉토리로 cd 후 `ninja` 커맨드를 입력해 실행하세요.

자체적으로 컨테이너 화 된 Alpine 리눅스 파일 시스템으로 실행하고 싶다면, [Alpine 웹사이트](https://alpinelinux.org/downloads/) 에서 i386을 위한 Alpine minirootfs(Mini Root Filesystem) tarball 을 다운로드 받고 `./tools/fakefsify`으로 실행하세요. 매개인자로 다운로드 받은 minirootfs tarball 파일을 입력하고 출력 받을 디렉토리의 이름을 두번째 인자로 입력하면 됩니다. 그 후에는 `./ish -f {출력받을 디렉토리 이름} /bin/login -f root` 명령어를 사용하여 Alpine 시스템 내에서 원하는 것을 실행할 수 있습니다. 만약 `tools/fakefsify` 가 빌드 디렉토리에 존재하지 않는다면, libarchive를 찾을 수 없어서 그런 것일 수 있습니다. 위를 참고하여 시스템에 설치하는 방법을 참고해주세요.
자체적으로 컨테이너 화 된 Alpine 리눅스 파일 시스템으로 실행하고 싶다면, [Alpine 웹사이트](https://alpinelinux.org/downloads/) 에서 i386을 위한 Alpine minirootfs(Mini Root Filesystem) tarball 을 다운로드 받고 `./tools/fakefsify`으로 실행하세요. 매개인자로 다운로드 받은 minirootfs tarball 파일을 입력하고 출력 받을 디렉토리의 이름을 두번째 인자로 입력하면 됩니다. 그 후에는 `./ish -f {출력받을 디렉토리 이름} /bin/sh` 명령어를 사용하여 Alpine 시스템 내에서 원하는 것을 실행할 수 있습니다. 만약 `tools/fakefsify` 가 빌드 디렉토리에 존재하지 않는다면, libarchive를 찾을 수 없어서 그런 것일 수 있습니다. 위를 참고하여 시스템에 설치하는 방법을 참고해주세요.

실제 프로세스로 프로그램을 실행하고 각 단계의 레지스터를 비교하기 위해서 `ish``tools/ptraceomatic`로 바꿔 실행할 수 있습니다. 디버깅을 위해 저는 사용합니다. 64-bit Linux 4.11 이후 버전이 필요합니다.

Expand All @@ -67,4 +67,4 @@ iSH에서 추가한 것 중 가장 흥미로운 것은 JIT 컴파일러 일 것

불행하게도 저는 어셈블리어로 대부분의 이러한 gadget을 작성했습니다. 이것은 성능적으로는 좋은 선택이었을 지 몰라도(실제로는 알 도리가 없지만), 가독성, 유지보수, 그리고 제 정신상태에 대해서는 좋지 않은 선택이 되었습니다. 컴파일러/어셈블러/링커로 인한 여러 고충은 말도 할 수 없을 정도입니다. 거의 무슨 제 코드의 가독성을 해치지 않으면 컴파일을 막는 그러한 악마가 있는 것 같았습니다. 이 코드를 작성하는 도중 제정신을 유지하기 위해서 저는 네이밍과 코드 구조론을 따른 최적의 선택을 하지 못하였습니다. `ss`, `s` 그리고 `a`와 같은 매크로 그리고 변수 명을 찾을 수 있을 것입니다. 주석 또한 찾기 힘들 것입니다.

그렇기에 주의 하세요: 해당 코드를 장기간 접할 경우 정신질환을 앓게되거나 GAS 매크로와 링커오류에 대한 악몽에 시달리고 또다른 부작용이 있을 수 있습니다. 암, 선천적 결함, 또는 생식기 질환을 야기한다고 질병관리청에서 인정했습니다. 암튼 그랬습니다.
그렇기에 주의 하세요: 해당 코드를 장기간 접할 경우 정신질환을 앓게되거나 GAS 매크로와 링커오류에 대한 악몽에 시달리고 또다른 부작용이 있을 수 있습니다. 암, 선천적 결함, 또는 생식기 질환을 야기한다고 질병관리청에서 인정했습니다. 암튼 그랬습니다.
4 changes: 2 additions & 2 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ iSH 是一个运行在 iOS 上的 Linux shell。本项目使用了 x86 用户模

在项目目录中运行命令 `meson build`,之后 `build` 目录会被创建。进入到 `build` 目录并运行命令 `ninja`

为了建立一个自有的 Alpine linux 文件系统,请从 [Alpine 网站](https://alpinelinux.org/downloads/) 下载 `Alpine minirotfs tarball for i386` 并运行 `tools/fakefsify` 。将 minirotfs tarball 指定为第一个参数,将输出目录的名称(如`alpine`)指定为第二个参数,即 `tools/fakefsify $MinirotfsTarballFilename alpine` 然后在 Alpine 文件系统中运行 `/ish -f alpine/bin/login -f root`。如果 `build` 目录下找不到 `tools/fakefsify`,可能是系统上找不到 `libarchive` 的依赖(请参照前面的章节进行安装)。
为了建立一个自有的 Alpine linux 文件系统,请从 [Alpine 网站](https://alpinelinux.org/downloads/) 下载 `Alpine minirotfs tarball for i386` 并运行 `tools/fakefsify` 。将 minirotfs tarball 指定为第一个参数,将输出目录的名称(如`alpine`)指定为第二个参数,即 `tools/fakefsify $MinirotfsTarballFilename alpine` 然后在 Alpine 文件系统中运行 `/ish -f alpine/bin/sh`。如果 `build` 目录下找不到 `tools/fakefsify`,可能是系统上找不到 `libarchive` 的依赖(请参照前面的章节进行安装)。

除了可以使用 `ish`,你也可以使用 `tools/ptraceomatic` 替代它,以便在某个真实进程中单步比较寄存器。我通常使用它来进行调试(需要 64 位 Linux 4.11 或更高版本)。

Expand All @@ -64,4 +64,4 @@ iSH 是一个运行在 iOS 上的 Linux shell。本项目使用了 x86 用户模

但不幸的是,我最开始决定用汇编语言编写几乎所有的 gadgets。这可能从性能方面来说是一个好的决定(虽然我永远也无法确定),但是对可读性、可维护性和我的理智来说,这是一个可怕的决定。我承受了大量来自编译器、汇编程序以及链接器的乱七八糟的东西。那里面就像有一个魔鬼,把我的代码搞得畸形,就算没有畸形,也会编造一些愚蠢的理由说它不能够编译。为了在编写代码时保持理智,我不得不忽略代码结构和命名方面的最佳实践。你会发现宏和变量具有诸如 `ss``s``a` 等描述性的名称,并且汇编器的宏嵌套层数超乎你的想象。最重要的是,代码中几乎没有任何注释。

所以这是一个警告: 长期接触此代码可能会使你失去理智,对 GAS 宏和链接器错误产生噩梦,或是任何其他使人虚弱的副作用。在加利福尼亚,众所周知这样的代码会导致癌症、生产缺陷和重复伤害。
所以这是一个警告: 长期接触此代码可能会使你失去理智,对 GAS 宏和链接器错误产生噩梦,或是任何其他使人虚弱的副作用。在加利福尼亚,众所周知这样的代码会导致癌症、生产缺陷和重复伤害。
38 changes: 34 additions & 4 deletions fs/pty.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,30 @@ static int pty_master_init(struct tty *tty) {
return 0;
}


static void pty_hangup(struct tty *tty) {
if (tty == NULL)
return;
lock(&tty->lock);
tty_hangup(tty);
unlock(&tty->lock);
}

static struct tty *pty_hangup_other(struct tty *tty) {
struct tty *other = tty->pty.other;
if (other == NULL)
return NULL;
pty_hangup(other);
return other;
}

static void pty_slave_cleanup(struct tty *tty) {
pty_hangup_other(tty);
}

static void pty_master_cleanup(struct tty *tty) {
struct tty *slave = tty->pty.other;
struct tty *slave = pty_hangup_other(tty);
slave->pty.other = NULL;
lock(&slave->lock);
tty_hangup(slave);
unlock(&slave->lock);
tty_release(slave);
}

Expand All @@ -51,6 +69,16 @@ static int pty_slave_open(struct tty *tty) {
return 0;
}

static int pty_slave_close(struct tty *tty) {
// If userland's reference count on the pty slave will go to 0,
// hang up the pty master. But the session leader may have a
// reference, and the pty master always has a reference.
if (tty->refcount - 1 == (tty->session ? 2 : 1)) {
pty_hangup_other(tty);
}
return 0;
}

static int pty_master_ioctl(struct tty *tty, int cmd, void *arg) {
struct tty *slave = tty->pty.other;
switch (cmd) {
Expand Down Expand Up @@ -94,7 +122,9 @@ DEFINE_TTY_DRIVER(pty_master, &pty_master_ops, TTY_PSEUDO_MASTER_MAJOR, MAX_PTYS
const struct tty_driver_ops pty_slave_ops = {
.init = pty_return_eio,
.open = pty_slave_open,
.close = pty_slave_close,
.write = pty_write,
.cleanup = pty_slave_cleanup,
};
DEFINE_TTY_DRIVER(pty_slave, &pty_slave_ops, TTY_PSEUDO_SLAVE_MAJOR, MAX_PTYS);

Expand Down
28 changes: 11 additions & 17 deletions fs/tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,7 @@ void tty_release(struct tty *tty) {
cond_destroy(&tty->produced);
free(tty);
} else {
// bit of a hack
struct tty *master = NULL;
if (tty->driver == &pty_slave && tty->refcount == 1)
master = tty->pty.other;
unlock(&tty->lock);
if (master != NULL) {
lock(&master->lock);
tty_poll_wakeup(master, POLL_READ | POLL_HUP);
unlock(&master->lock);
}
}
}

Expand Down Expand Up @@ -207,11 +198,14 @@ static int tty_device_open(int major, int minor, struct fd *fd) {

static int tty_close(struct fd *fd) {
if (fd->tty != NULL) {
lock(&fd->tty->fds_lock);
struct tty *tty = fd->tty;
lock(&tty->fds_lock);
list_remove_safe(&fd->tty_other_fds);
unlock(&fd->tty->fds_lock);
unlock(&tty->fds_lock);
lock(&ttys_lock);
tty_release(fd->tty);
if (tty->driver->ops->close)
tty->driver->ops->close(tty);
tty_release(tty);
unlock(&ttys_lock);
}
return 0;
Expand Down Expand Up @@ -414,7 +408,7 @@ static bool pty_is_half_closed_master(struct tty *tty) {
struct tty *slave = tty->pty.other;
// only time one tty lock is nested in another
lock(&slave->lock);
bool half_closed = slave->ever_opened && slave->refcount == 1;
bool half_closed = slave->ever_opened && (slave->refcount == 1 || slave->hung_up);
unlock(&slave->lock);
return half_closed;
}
Expand Down Expand Up @@ -450,7 +444,7 @@ static ssize_t tty_read(struct fd *fd, void *buf, size_t bufsize) {
struct tty *tty = fd->tty;
lock(&pids_lock);
lock(&tty->lock);
if (tty->hung_up) {
if (tty->hung_up || pty_is_half_closed_master(tty)) {
unlock(&pids_lock);
goto error;
}
Expand Down Expand Up @@ -546,7 +540,7 @@ static ssize_t tty_read(struct fd *fd, void *buf, size_t bufsize) {
static ssize_t tty_write(struct fd *fd, const void *buf, size_t bufsize) {
struct tty *tty = fd->tty;
lock(&tty->lock);
if (tty->hung_up) {
if (tty->hung_up || pty_is_half_closed_master(tty)) {
unlock(&tty->lock);
return _EIO;
}
Expand Down Expand Up @@ -672,7 +666,7 @@ static int tiocgpgrp(struct tty *tty, pid_t_ *fg_group) {
lock(&slave->lock);
}

if (tty == slave && !tty_is_current(slave) || slave->fg_group == 0) {
if (tty == slave && (!tty_is_current(slave) || slave->fg_group == 0)) {
err = _ENOTTY;
goto error_no_ctrl_tty;
}
Expand Down Expand Up @@ -800,7 +794,7 @@ void tty_set_winsize(struct tty *tty, struct winsize_ winsize) {

void tty_hangup(struct tty *tty) {
tty->hung_up = true;
tty_poll_wakeup(tty, POLL_READ | POLL_WRITE | POLL_ERR | POLL_HUP);
tty_input_wakeup(tty);
}

struct dev_ops tty_dev = {
Expand Down
1 change: 1 addition & 0 deletions fs/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ struct tty_driver {
struct tty_driver_ops {
int (*init)(struct tty *tty);
int (*open)(struct tty *tty);
int (*close)(struct tty *tty);
int (*write)(struct tty *tty, const void *buf, size_t len, bool blocking);
int (*ioctl)(struct tty *tty, int cmd, void *arg);
void (*cleanup)(struct tty *tty);
Expand Down
1 change: 1 addition & 0 deletions ish-lldb.lldb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
process handle -n 0 -p 1 -s 0 SIGUSR1 SIGTTIN SIGPIPE
28 changes: 22 additions & 6 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,28 @@ noreturn void do_exit_group(int status) {

// always called from init process
static void halt_system(void) {
// brutally murder everything
// which will leave everything in an inconsistent state. I will solve this problem later.
for (int i = 2; i < MAX_PID; i++) {
struct task *task = pid_get_task(i);
if (task != NULL)
pthread_kill(task->thread, SIGKILL);
for (int state = 0; state < 3; state++) {
int tasks_found = 0;
for (int i = 2; i < MAX_PID; i++) {
struct task *task = pid_get_task(i);
if (task != NULL) {
tasks_found++;
switch (state) {
case 0:
deliver_signal(task, SIGTERM_, SIGINFO_NIL);
break;
case 1:
deliver_signal(task, SIGKILL_, SIGINFO_NIL);
break;
case 2:
pthread_kill(task->thread, SIGTERM);
}
}
}
if (tasks_found == 0)
break;
if (state != 2)
sleep(1);
}

// unmount all filesystems
Expand Down
4 changes: 4 additions & 0 deletions kernel/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ static void log_line(const char *line) {
static void log_line(const char *line) {
os_log_fault(OS_LOG_DEFAULT, "%s", line);
}
#elif LOG_HANDLER_STDERR
static void log_line(const char *line) {
fprintf(stderr, "%s\n", line);
}
#endif

static void default_die_handler(const char *msg) {
Expand Down

0 comments on commit cee7abd

Please sign in to comment.