[LibOS,Pal] Support Glibc logic of line-buffered/fully-buffered STDOUT

Glibc has special handling of STDOUT: it checks whether STDOUT is
TTY (app writes to a terminal) or non-TTY (app writes to a pipe/file)
and makes STDOUT line-buffered or fully-buffered respectively. Glibc
uses a trick with IOCTL(TCGETS) to find out whether it is TTY or
non-TTY. Previously, Graphene always returned -EINVAL on this IOCTL,
so STDOUT in Graphene was always fully-buffered. This inconsistency
led to e.g. close(STDOUT) without printing out buffered strings.
This commit fixes this by propagating info on TTY/non-TTY from the
host.
This commit is contained in:
Dmitrii Kuvaiskii
2020-04-13 19:01:07 -07:00
parent a783fa2f4b
commit f609caf10a
9 changed files with 62 additions and 10 deletions
+14 -7
View File
@@ -41,7 +41,7 @@
#define TERM_DEFAULT_CFLAG (B38400 | CS8 | CREAD)
#define TERM_DEFAULT_LFLAG (ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN)
static int ioctl_termios(struct shim_handle* hdl, unsigned int cmd, unsigned long arg) {
static int ioctl_termios(int fd, struct shim_handle* hdl, unsigned int cmd, unsigned long arg) {
if (hdl->type != TYPE_FILE || hdl->info.file.type != FILE_TTY)
return -ENOTTY;
@@ -58,14 +58,21 @@ static int ioctl_termios(struct shim_handle* hdl, unsigned int cmd, unsigned lon
return -EINVAL;
case TCGETS: {
#if 0
struct termios * termios = (struct termios *) arg;
/* proactively set termios, it doesn't hurt */
struct termios* termios = (struct termios*)arg;
termios->c_iflag = TERM_DEFAULT_IFLAG;
termios->c_oflag = TERM_DEFAULT_OFLAG;
termios->c_cflag = TERM_DEFAULT_CFLAG;
termios->c_lflag = TERM_DEFAULT_LFLAG;
return 0;
#endif
if (fd == STDOUT_FILENO) {
/* special case: Glibc wants to know whether STDOUT is TTY (app writes to terminal)
* or non-TTY (app writes to pipe/file); Graphene buffers this info in PAL's
* is_stdout_tty field */
if (PAL_CB(is_stdout_tty))
return 0;
}
return -EINVAL;
}
@@ -346,7 +353,7 @@ int shim_do_ioctl(int fd, unsigned long cmd, unsigned long arg) {
case TIOCSETD:
case TIOCGETD:
case TCSBRKP:
ret = ioctl_termios(hdl, cmd, arg);
ret = ioctl_termios(fd, hdl, cmd, arg);
break;
case FIONBIO:
if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->setflags)
@@ -374,7 +381,7 @@ int shim_do_ioctl(int fd, unsigned long cmd, unsigned long arg) {
case TIOCSERGETLSR:
case TIOCSERGETMULTI:
case TIOCSERSETMULTI:
ret = ioctl_termios(hdl, cmd, arg);
ret = ioctl_termios(fd, hdl, cmd, arg);
break;
case FDCLRPRM:
+5
View File
@@ -341,6 +341,11 @@ typedef struct PAL_CONTROL_ {
PAL_CPU_INFO cpu_info; /*!< CPU information (only required ones) */
PAL_MEM_INFO mem_info; /*!< memory information (only required ones) */
/*
* Misc
*/
PAL_BOL is_stdout_tty; /*!< STDOUT is TTY (app prints to term) or non-TTY (to pipe/file) */
} PAL_CONTROL;
#define pal_control (*pal_control_addr())
+1
View File
@@ -395,6 +395,7 @@ noreturn void pal_main (
__pal_control.host_type = XSTRINGIFY(HOST_TYPE);
__pal_control.process_id = _DkGetProcessId();
__pal_control.host_id = _DkGetHostId();
__pal_control.is_stdout_tty = _DkIsStdoutTty();
__pal_control.manifest_handle = manifest_handle;
__pal_control.executable = exec_uri;
__pal_control.parent_process = parent_process;
+6
View File
@@ -82,6 +82,11 @@ PAL_NUM _DkGetHostId (void)
return 0;
}
PAL_BOL _DkIsStdoutTty(void) {
/* is_stdout_tty is initialized in untrusted runtime during enclave creation and buffered */
return pal_sec.is_stdout_tty;
}
#include "elf-x86_64.h"
#include "dynamic_link.h"
#include <asm/errno.h>
@@ -272,6 +277,7 @@ void pal_linux_main(char * uptr_args, uint64_t args_size,
COPY_ARRAY(pal_sec.pipe_prefix, sec_info.pipe_prefix);
pal_sec.qe_targetinfo = sec_info.qe_targetinfo;
pal_sec.is_stdout_tty = sec_info.is_stdout_tty;
#ifdef DEBUG
pal_sec.in_gdb = sec_info.in_gdb;
#endif
+3
View File
@@ -52,6 +52,9 @@ struct pal_sec {
/* Need to pass in the number of cores */
PAL_NUM num_cpus;
/* STDOUT is TTY (app prints to term) or non-TTY (app prints to pipe/file) */
PAL_BOL is_stdout_tty;
#ifdef DEBUG
PAL_BOL in_gdb;
#endif
+13 -3
View File
@@ -10,16 +10,17 @@
#include "sgx_internal.h"
#include "sgx_tls.h"
#include <asm/errno.h>
#include <asm/fcntl.h>
#include <asm/ioctls.h>
#include <asm/socket.h>
#include <ctype.h>
#include <linux/fs.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <asm/errno.h>
#include <ctype.h>
#include <sysdep.h>
#include <sysdeps/generic/ldsodefs.h>
#include <termios.h>
size_t g_page_size = PRESET_PAGESIZE;
@@ -789,6 +790,15 @@ static int load_enclave (struct pal_enclave * enclave,
}
#endif
struct termios term;
ret = INLINE_SYSCALL(ioctl, 3, STDOUT_FILENO, TCGETS, &term);
if (IS_ERR(ret)) {
/* mark STDOUT as not TTY (app writes to pipe/file); Glibc considers it fully-buffered */
pal_sec->is_stdout_tty = false;
}
/* mark STDOUT as TTY (app writes to terminal); Glibc considers it line-buffered */
pal_sec->is_stdout_tty = true;
enclave->manifest = manifest_fd;
ret = load_manifest(enclave->manifest, &enclave->config);
+13
View File
@@ -37,6 +37,7 @@
#include <asm/mman.h>
#include <elf/elf.h>
#include <sysdeps/generic/ldsodefs.h>
#include <termios.h>
/* At the begining of entry point, rsp starts at argc, then argvs,
envps and auxvs. Here we store rsp to rdi, so it will not be
@@ -188,6 +189,18 @@ PAL_NUM _DkGetHostId (void)
return 0;
}
PAL_BOL _DkIsStdoutTty(void) {
struct termios term;
int ret = INLINE_SYSCALL(ioctl, 3, STDOUT_FILENO, TCGETS, &term);
if (IS_ERR(ret)) {
/* mark STDOUT as not TTY (app writes to pipe/file); Glibc considers it fully-buffered */
return false;
}
/* mark STDOUT as TTY (app writes to terminal); Glibc considers it line-buffered */
return true;
}
#include "dynamic_link.h"
void setup_pal_map (struct link_map * map);
+6
View File
@@ -56,6 +56,12 @@ PAL_NUM _DkGetHostId (void)
return 0;
}
PAL_BOL _DkIsStdoutTty(void) {
/* mark STDOUT as not TTY (app writes to pipe/file); Glibc considers it fully-buffered */
return false;
}
int _DkGetCPUInfo (PAL_CPU_INFO * ci)
int _DkGetCPUInfo (PAL_CPU_INFO * ci)
{
/* needs to be implemented */
+1
View File
@@ -219,6 +219,7 @@ void _DkGetAvailableUserAddressRange (PAL_PTR * start, PAL_PTR * end, PAL_PTR *
bool _DkCheckMemoryMappable (const void * addr, size_t size);
PAL_NUM _DkGetProcessId (void);
PAL_NUM _DkGetHostId (void);
PAL_BOL _DkIsStdoutTty(void);
unsigned long _DkMemoryQuota (void);
unsigned long _DkMemoryAvailableQuota (void);
// Returns 0 on success, negative PAL code on failure