mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-06 05:41:37 +00:00
[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:
@@ -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:
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user