context&rnd:needs cleanup/eac:done

This commit is contained in:
steveej 2017-09-29 10:03:02 +02:00
parent f57961fb84
commit 427ef068c5
5 changed files with 521 additions and 333 deletions

View file

@ -108,7 +108,6 @@
\newglossaryentry{stack}{ \newglossaryentry{stack}{
name = stack, name = stack,
description = {% description = {%
TODO
}, },
} }
@ -122,7 +121,6 @@
\newglossaryentry{heap}{ \newglossaryentry{heap}{
name = heap, name = heap,
description = {% description = {%
TODO
}, },
} }
@ -148,42 +146,36 @@
\newglossaryentry{fs}{ \newglossaryentry{fs}{
name = filesystem, name = filesystem,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{virt}{ \newglossaryentry{virt}{
name = virtualization, name = virtualization,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{OSS}{ \newglossaryentry{OSS}{
name = Open-Source Software, name = Open-Source Software,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{osvirt}{ \newglossaryentry{osvirt}{
name = Operating System-Level Virtualization, name = Operating System-Level Virtualization,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{hypervisor}{ \newglossaryentry{hypervisor}{
name = Hypervisor, name = Hypervisor,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{VM}{ \newglossaryentry{VM}{
name = Virtual Machine, name = Virtual Machine,
description = {% description = {%
TODO
}, },
} }
@ -213,35 +205,30 @@
\newglossaryentry{imezzos}{ \newglossaryentry{imezzos}{
name = intermezzOS, name = intermezzOS,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{redoxos}{ \newglossaryentry{redoxos}{
name = Redox OS, name = Redox OS,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{blogos}{ \newglossaryentry{blogos}{
name = Blog OS, name = Blog OS,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{tockos}{ \newglossaryentry{tockos}{
name = Tock OS, name = Tock OS,
description = {% description = {%
TODO
}, },
} }
\newglossaryentry{rootfs}{ \newglossaryentry{rootfs}{
name = RootFS, name = RootFS,
description = {% description = {%
% TODO
}, },
} }
@ -270,7 +257,6 @@
\newglossaryentry{BSD}{ \newglossaryentry{BSD}{
name = BSD, name = BSD,
description = {% description = {%
TODO
} }
} }
@ -287,7 +273,6 @@
\newglossaryentry{pm}{ \newglossaryentry{pm}{
name = package manager, name = package manager,
description = {% description = {%
TODO
} }
} }
@ -318,7 +303,6 @@
\newglossaryentry{LXC}{ \newglossaryentry{LXC}{
name = LXC, name = LXC,
description = {% description = {%
TODO
} }
} }
@ -332,14 +316,12 @@
\newglossaryentry{systemd-nspawn}{ \newglossaryentry{systemd-nspawn}{
name = systemd-nspawn, name = systemd-nspawn,
description = {% description = {%
TODO
} }
} }
\newglossaryentry{rkt}{ \newglossaryentry{rkt}{
name = rkt, name = rkt,
description = {% description = {%
TODO
} }
} }
@ -410,7 +392,7 @@
\newglossaryentry{C}{ \newglossaryentry{C}{
name = C, name = C,
, description = {% , description = {%
TODO C programming language, C programming language,
} }
} }
@ -422,9 +404,9 @@
} }
\newglossaryentry{asm}{ \newglossaryentry{asm}{
name = Assembly programming language, name = ASM,
long = Assembly programming language,
description = {% description = {%
TODO ASM
} }
} }
@ -432,7 +414,7 @@
name = AMD64, name = AMD64,
long = AMD64, long = AMD64,
description = {% description = {%
TODO AMD64 Contemporary Hardware Architecture\cite{AMD64Vol1,AMD64Vol2}
}, },
first = {\glsentrylong{amd64}}, first = {\glsentrylong{amd64}},
} }
@ -441,7 +423,7 @@
name = CPU, name = CPU,
long = Central Processing Unit, long = Central Processing Unit,
description = {% description = {%
TODO cpu Central Haddware Unit that executes machine code
}, },
first = {\glsentrylong{cpu}}, first = {\glsentrylong{cpu}},
} }
@ -451,7 +433,6 @@
name = TLB, name = TLB,
long = Translation Lookaside Buffer, long = Translation Lookaside Buffer,
description = {% description = {%
TODO tlb
}, },
first = {\glsentrylong{tlb}}, first = {\glsentrylong{tlb}},
} }
@ -476,7 +457,6 @@
\newglossaryentry{sysadmin}{ \newglossaryentry{sysadmin}{
name = System Administrator name = System Administrator
, description = {% , description = {%
TODO sysadmin
} }
} }

View file

@ -19,7 +19,7 @@ The hypothesis cannot be trivially approved or denied, which drives the research
Besides this specific hypothesis, many implementations of \glspl{os} with \gls{Rust} have appeared in public. Besides this specific hypothesis, many implementations of \glspl{os} with \gls{Rust} have appeared in public.
Their purposes range from proof-of-concept and educational work like \gls{imezzos} and \gls{blogos}, to implementations that aim to be production grade software like \gls{redoxos} and \gls{tockos} \cite{Levy2015a}. Their purposes range from proof-of-concept and educational work like \gls{imezzos} and \gls{blogos}, to implementations that aim to be production grade software like \gls{redoxos} and \gls{tockos} \cite{Levy2015a}.
These implementations are subject to evaluation in \cref{rnd::existing-os-dev-with-rust} These implementations are subject to evaluation in \cref{rnd::existing-os-dev-with-rust}.
The final results will be of qualitative nature, captured by analyzing the existing and a self-developed \gls{Rust}-implementations of popular memory management techniques. The final results will be of qualitative nature, captured by analyzing the existing and a self-developed \gls{Rust}-implementations of popular memory management techniques.
In addition to the sole analysis of \gls{Rust}-implementations, comparisons will be made, discerning the level of memory safety guarantees gained over similarly intending implementations in \gls{C}. In addition to the sole analysis of \gls{Rust}-implementations, comparisons will be made, discerning the level of memory safety guarantees gained over similarly intending implementations in \gls{C}.
@ -1245,7 +1245,7 @@ The following program prints a string provided as an argument.
\FloatBarrier \FloatBarrier
The example is exploitable, because of the call to printf() in the printWrapper() function. Note: The stack buffer was added to make exploitation more simple. The example is exploitable, because of the call to printf() in the printWrapper() function. Note: The stack buffer was added to make exploitation more simple.
\subsection{Heartbleed} % \subsection{Heartbleed}
% TODO: paper about hearbleed with Rust % TODO: paper about hearbleed with Rust
\subsection{BlueBorne on Linux} \subsection{BlueBorne on Linux}

View file

@ -1,215 +1,246 @@
% // vim: set ft=tex: % // vim: set ft=tex:
\chapter{Summary} \chapter{Evaluation}
Solved problems by the Rust ownership models are This chapter summarizes the findings of the previous parts.
The Rust ownership model solves The summary is then evaluated against the hypothesis, to create the foundation of a concise conclusion.
\section{Summary}
After defining an exact definition for memory-safety within the OS was found in \cref{context::introduction::memory-safety::def}, various aspects of software vulnerability origin were discussed.
The human was identified to be an error prone weak spot in the process of OS development.
It was found that technical solutions that can detect these errors are to be used as early in the development process as possible.
This point in time was declared the time of software compilation.
OS development concepts were introduced to for the AMD64 architecture, to lay out the knowledge that to allow an understanding and implementation of OS concepts on AMD64, which was set out for the development process.
Common Weaknesses in software were identified, and demonstrated how these lead to concrete vulnerabilities.
The stack clash was explained as an architectural and design issue, which requires changes in stack overflow detection in userspace software.
The origin of many of the weaknesses was identified to be based on weak languages, and Rust was verified to be a good alternative to C.
Research was conducted on these common weaknesses through other scientific studies.
This found weaknesses based on
\begin{itemize} \begin{itemize}
\item use-after-free \item use-after-free
\item indexing out of bounds \item indexing out of bounds
\item iterator invalidation \item iterator invalidation
\item data races \item data races
\end{itemize} \end{itemize}
to be prevented by Rust's ownership system\cite{Beingessner2015}.
Stack protection experiments were conducted and found Rust to be less vulnerable to return address manipulation.
These were found to be effectively prevented by static analysis under normal circumstances, since it required multiple explicit features to intentionally force the manipulation to succeed.
Stack overflow could not be statically detected by trying various tweaks to the compiler.
Information that would be required for this static detection was evaluated, and was found to be not completely available in the present compiler architecture.
A practical introduction to Rust was given, overviewing the encountered language features and the ones that were explicitly investigated.
Rust was found to have extension features only limited by the complexity of its usage, demonstrated by an implementation of pure-software information flow control.
Existing OS development efforts were investigated to serve as a codebase for the development and to evaluate their usage of Rust for achieving memory-safety.
Redox OS was found to not be vulnerable to the Stack Clash due to design decisions in the OS.
Blog OS was found to demonstrate extensive usage of Rust's type system to model underlying hardware and prevent mistakes in the paging implementation.
Implementation of preemptive multitasking was chosen to be based on intermezzOS for its simplicity.
After initial problems with the build and debugging tools were solved, the development could proceed quickly.
Based on the state of intermezzOS that allowed the system to boot, a working preemptive multitasking was implemented successfully.
The implementation only supports static memory allocation and no dynamic memory management.
Writing a hardware-driver for the Programmable-Interrupt-Timer was well supported by the module and type system, which allowed an accurate modeling of the underlying hardware.
Global OS state variables can be protected by requiring Rust's unsafe keyword and disallowing the same within additional defined tasks.
Extensive usage of the unsafe keyword was required to perform raw hardware access, but could be limited to well-defined functions.
Inline machine-instructions were found to be well designed and in-line with the rest of the language.
One occurrence of a cast from an untyped pointer was necessary, within the context-switching interrupt handler, to manipulate data on the stack.
A stack overflow on user defined tasks could not be prevented by static analysis, only detected by the OS at runtime.
\subsection{Thesis Evaluation}
Rust's static analysis lacks the ability of static stack overflow detection, which is a significant counter-indication to the hypothesis.
Using Rust's static memory analysis does not fully guarantee In-Kernel memory-safe.
\chapter{Conclusions} \chapter{Conclusions}
Safety - or security for this matter - is not something that can be achieved absolutely. While hypothesis was not proven, Rust is still considered to be a significant improvement over C for OS development purposes.
It grows successively and gives the \gls{os} developers and the end-users a \emph{feeling} of safety, until another vulnerability is found and disclosed.
\section{Trust In Hardware} \paragraph{Rust detects many errors early}
Memory management mechanisms are partially implemented in the target system's hardware which can't be verified by at time of development. It prevents many errors at compile-time where, they are harmless.
The language is fully extendable via language extensions that allow the insertion of new language features that can be hooked into the static analysis.
The process of making Rust suitable for OS development is driven by many hobby and a few production intended projects.
\subsection{The Necessary Evils of \textit{unsafe}} \section{Hardware is still hard, but Rust is worth learning}
TODO Even though Rust is understood as a memory-safe language, following the hardware specification is still a memory-safety critical requirement.
OS developers must use the unsafe keyword when performing raw hardware access, which is designed to make them think twice when using it.
If the chance is presented, Rust should be chosen any time over C for implementing software that is close to hardware.
This might be difficult in the first place, but should pay off long-term, as less vulnerabilities will be detected throughout the extended life-cycle of the software.
\section{Rust Is Extendable For Memory-Safe OS Development} \section{Next Step}
Further investigation is required to propose a solution for the lack of static stack size estimation in \gls{Rust}.
The immediate next step is to bring this issue up for a discussion in the Rust community.
* Macros help to prevent critical mistakes when defining interrupt handlers. % \chapter{Scratchpad}
-
TODO
\section{Thesis Evaluation}
% TODO: repeat that rust *can* be used to increase safety in the OS,
% TODO: how?
% but it doesn't guarantee it per-se
\section{Rust's Strong Community}
\section{Next Steps}
TODO
\chapter{Scratchpad}
\begin{figure}[ht!]
\centering
\begin{subfigure}[T]{0.50\textwidth}
\tikzmarkcountprep{callee}
\begin{compactminted}[
escapeinside=??,linenos,autogobble,highlightlines={}
]{nasm}
mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount?
add rsp,0x50?\tikzmarkcount?
pop rbp?\tikzmarkcount?
ret?\tikzmarkcount?
\end{compactminted}
\tikzmarkdrawcircles
\caption{Subfig A}
\end{subfigure}
\begin{subfigure}[T]{0.45\textwidth}
\foreach \x/\xtext in {
1/{
this is going to be a really long sentence with line wraps
},
2/{
second
}
} {\tikzmarkcircle{\x}\xtext\\}
\caption{Subfig B}
\end{subfigure}
\caption{Whadup}
\label{Whadup}
\end{figure}
\begin{listing}
\tikzmarkcountprep{example1}
\begin{minted}[
label=example1,labelposition=all,escapeinside=??,linenos,autogobble,highlightlines={}
]{nasm}
mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount? ?\tikzmark{brace1upper}?
add rsp,0x50?\tikzmarkcount?
pop rbp?\tikzmarkcount?
ret?\tikzmarkcount? ?\tikzmark{brace1lower}?
\end{minted}
\begin{minted}[
escapeinside=??,linenos,autogobble,highlightlines={}
]{nasm}
mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount?
add rsp,0x50 ?\tikzmarkcount?
pop rbp ?\tikzmarkcount?
ret ?\tikzmarkcount?
\end{minted}
\begin{tikzpicture}[remember picture,overlay]
\draw[thick,decorate,decoration={brace,raise=1ex}]
(pic cs:brace1upper)+(0,1.5ex) -- node[shape=coordinate][right=1.5ex] (a) {} (pic cs:brace1lower);
\fill (a)+(2ex,0) circle[opacity=1,radius=1.1ex] node[white,font=\small]{a};
\end{tikzpicture}
\tikzmarkdrawcircles
\caption{Minted Listing A}
% %
\foreach \x/\xtext in { % \begin{figure}[ht!]
1/{ % \centering
this is going to be a really long sentence with line wraps % \begin{subfigure}[T]{0.50\textwidth}
\\} % \tikzmarkcountprep{callee}
,2/{ % \begin{compactminted}[
second % escapeinside=??,linenos,autogobble,highlightlines={}
\\} % ]{nasm}
,5/{},6/{ % mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount?
hi % add rsp,0x50?\tikzmarkcount?
\\} % pop rbp?\tikzmarkcount?
,a/{ % ret?\tikzmarkcount?
hi % \end{compactminted}
\\} % \tikzmarkdrawcircles
} {\tikzmarkcircle{\x}\xtext} % \caption{Subfig A}
% \end{subfigure}
% \begin{subfigure}[T]{0.45\textwidth}
% \foreach \x/\xtext in {
% 1/{
% this is going to be a really long sentence with line wraps
% },
% 2/{
% second
% }
% } {\tikzmarkcircle{\x}\xtext\\}
% \caption{Subfig B}
% \end{subfigure}
% \caption{Whadup}
% \label{Whadup}
% \end{figure}
% %
\end{listing} % \begin{listing}
\FloatBarrier % \tikzmarkcountprep{example1}
% \begin{minted}[
\begin{listing} % label=example1,labelposition=all,escapeinside=??,linenos,autogobble,highlightlines={}
\tikzset{/minted/basename=example} % ]{nasm}
\begin{minted}[label=caller,labelposition=topline,escapeinside=??,highlightlines={},autogobble,linenos,breaklines=true]{nasm} % mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount? ?\tikzmark{brace1upper}?
mov rcx,QWORD PTR [rbp-0x40] ; copy 1st arg to rcx % add rsp,0x50?\tikzmarkcount?
mov rsi,QWORD PTR [rbp-0x38] ; copy 2nd arg to rsi % pop rbp?\tikzmarkcount?
mov rdx,QWORD PTR [rbp-0x30] ; copy 3rd arg to rdx % ret?\tikzmarkcount? ?\tikzmark{brace1lower}?
mov QWORD PTR [rbp-0x60],rdi ; save rdi to make it available % \end{minted}
mov rdi,rcx ; copy 1st arg to rdi % \begin{minted}[
mov QWORD PTR [rbp-0x68],rax ; save rax to make it available % escapeinside=??,linenos,autogobble,highlightlines={}
call 7490?\tikzmark{exampleprecallfrom}? <_ZN14stack_handling3sum17h8f12d2383e075691E> ; push '756e' onto the stack and jump to the first instruction of sum % ]{nasm}
mov QWORD PTR [rbp-0x28],rax ; save return value % mov rax,QWORD PTR [rbp-0x48]?\tikzmarkcount?
\end{minted} % add rsp,0x50 ?\tikzmarkcount?
\caption{Function Call with Three Arguments} % pop rbp ?\tikzmarkcount?
\begin{tikzpicture}[remember picture,overlay] % ret ?\tikzmarkcount?
\draw[red,thick] (pic cs:exampleprecallfrom) ellipse (0.7cm and 12pt) node { \textbf{1} }; % \end{minted}
\fill[blue] (pic cs:example1) circle (0.1cm); % \begin{tikzpicture}[remember picture,overlay]
\fill[yellow] (pic cs:example2) circle (0.1cm); % \draw[thick,decorate,decoration={brace,raise=1ex}]
\end{tikzpicture} % (pic cs:brace1upper)+(0,1.5ex) -- node[shape=coordinate][right=1.5ex] (a) {} (pic cs:brace1lower);
\end{listing} % \fill (a)+(2ex,0) circle[opacity=1,radius=1.1ex] node[white,font=\small]{a};
% \end{tikzpicture}
\begin{tikzpicture}[node distance=2cm, % \tikzmarkdrawcircles
startstop/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30}, % \caption{Minted Listing A}
io/.style = {trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30}, % %
process/.style = {rectangle, minimum width=1cm, minimum height=1cm, text centered, text width=3cm, draw=black, fill=orange!30}, % \foreach \x/\xtext in {
decision/.style = {diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30}, % 1/{
arrow/.style = {thick,->,>=stealth} % this is going to be a really long sentence with line wraps
] % \\}
% ,2/{
%\node (start) [startstop] {Start}; % second
%\node (in1) [io, below of=start] {Input}; % \\}
%\node (pro1) [process, below of=in1] {Process 1}; % ,5/{},6/{
%\node (dec1) [decision, below of=pro1, yshift=-0.5cm] {Decision 1}; % hi
%\node (pro2a) [process, below of=dec1, yshift=-0.5cm] {Process 2a text text text text text text text text text text}; % \\}
%\node (pro2b) [process, right of=dec1, xshift=2cm] {Process 2b}; % ,a/{
%\node (out1) [io, below of=pro2a] {Output}; % hi
%\node (stop) [startstop, below of=out1] {Stop}; % \\}
% } {\tikzmarkcircle{\x}\xtext}
% %
% \end{listing}
% \FloatBarrier
% %
%\draw [arrow] (start) -- (in1); % \begin{listing}
%\draw [arrow] (in1) -- (pro1); % \tikzset{/minted/basename=example}
%\draw [arrow] (pro1) -- (dec1); % \begin{minted}[label=caller,labelposition=topline,escapeinside=??,highlightlines={},autogobble,linenos,breaklines=true]{nasm}
%\draw [arrow] (dec1) -- node[anchor=east] {yes} (pro2a); % mov rcx,QWORD PTR [rbp-0x40] ; copy 1st arg to rcx
%\draw [arrow] (dec1) -- node[anchor=south] {no} (pro2b); % mov rsi,QWORD PTR [rbp-0x38] ; copy 2nd arg to rsi
%\draw [arrow] (pro2b) |- (pro1); % mov rdx,QWORD PTR [rbp-0x30] ; copy 3rd arg to rdx
%\draw [arrow] (pro2a) -- (out1); % mov QWORD PTR [rbp-0x60],rdi ; save rdi to make it available
%\draw [arrow] (out1) -- (stop); % mov rdi,rcx ; copy 1st arg to rdi
% mov QWORD PTR [rbp-0x68],rax ; save rax to make it available
\node[process,xshift=0ex,yshift=-0ex] (ua_back) {User Applications}; % call 7490?\tikzmark{exampleprecallfrom}? <_ZN14stack_handling3sum17h8f12d2383e075691E> ; push '756e' onto the stack and jump to the first instruction of sum
\node[process,xshift=0ex,yshift=-1ex] at (ua_back) {User Applications}; % mov QWORD PTR [rbp-0x28],rax ; save return value
\node[process,xshift=0ex,yshift=-2ex] (ua) at (ua_back) {User Applications}; % \end{minted}
% \caption{Function Call with Three Arguments}
\node[process,xshift=0ex,yshift=-0ex,below of=ua] (sl_back) {System Libraries}; % \begin{tikzpicture}[remember picture,overlay]
\node[process,xshift=0ex,yshift=-1ex] at (sl_back) {System Libraries}; % \draw[red,thick] (pic cs:exampleprecallfrom) ellipse (0.7cm and 12pt) node { \textbf{1} };
\node[process,xshift=0ex,yshift=-2ex] (sl) at (sl_back) {System Libraries}; % \fill[blue] (pic cs:example1) circle (0.1cm);
% \fill[yellow] (pic cs:example2) circle (0.1cm);
\node[process,xshift=0ex,yshift=-0ex,below of=sl] (os_back) {OS}; % \end{tikzpicture}
\node[process,xshift=0ex,yshift=-1ex] at (os_back) {OS API}; % \end{listing}
\node[process,xshift=0ex,yshift=-2ex] (os) at (os_back) {OS}; %
% \begin{tikzpicture}[node distance=2cm,
\node[process,xshift=0ex,yshift=-0ex,left of=mem, below of=os] (cpu) {CPU}; % startstop/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
\node[process,xshift=0ex,yshift=-0ex,right of=cpu] (mem) {Memory}; % io/.style = {trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
\node[process,xshift=0ex,yshift=-0ex,right of=mem] (otherhw) {Other HW}; % process/.style = {rectangle, minimum width=1cm, minimum height=1cm, text centered, text width=3cm, draw=black, fill=orange!30},
% decision/.style = {diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
\draw [arrow] (ua) -- (sl); % arrow/.style = {thick,->,>=stealth}
\draw [arrow] (sl) -- (os); % ]
\draw [arrow] (os) -- (cpu); %
\draw [arrow] (os) -- (mem); % %\node (start) [startstop] {Start};
\draw [arrow] (os) -- (otherhw); % %\node (in1) [io, below of=start] {Input};
% %\node (pro1) [process, below of=in1] {Process 1};
TODO: improve % %\node (dec1) [decision, below of=pro1, yshift=-0.5cm] {Decision 1};
% %\node (pro2a) [process, below of=dec1, yshift=-0.5cm] {Process 2a text text text text text text text text text text};
\end{tikzpicture} % %\node (pro2b) [process, right of=dec1, xshift=2cm] {Process 2b};
% %\node (out1) [io, below of=pro2a] {Output};
\begin{markdown} % %\node (stop) [startstop, below of=out1] {Stop};
# Flow of Reasoning % %
* How to mitigate distributed weaknesses % %\draw [arrow] (start) -- (in1);
- Don't distribute vulnerable software % %\draw [arrow] (in1) -- (pro1);
- Produce less vulnerabilities % %\draw [arrow] (pro1) -- (dec1);
OR % %\draw [arrow] (dec1) -- node[anchor=east] {yes} (pro2a);
- Detect vulnerabilities % %\draw [arrow] (dec1) -- node[anchor=south] {no} (pro2b);
* How to prevent vulnerabilities distribution? % %\draw [arrow] (pro2b) |- (pro1);
- Human to make less mistakes; NOT VIABLE, see human aspect. % %\draw [arrow] (pro2a) -- (out1);
- Detect them before the \gls{app} is installed; see time aspect % %\draw [arrow] (out1) -- (stop);
* How to detect vulnerabilities %
- Write runtime tests for the program % \node[process,xshift=0ex,yshift=-0ex] (ua_back) {User Applications};
- Analyze the source code % \node[process,xshift=0ex,yshift=-1ex] at (ua_back) {User Applications};
* Runtime Tests % \node[process,xshift=0ex,yshift=-2ex] (ua) at (ua_back) {User Applications};
- Runs on every execution, thus wastes \gls{cpu} resources %
- Program needs to handle it % \node[process,xshift=0ex,yshift=-0ex,below of=ua] (sl_back) {System Libraries};
-> Slow and too late in the software life cycle! % \node[process,xshift=0ex,yshift=-1ex] at (sl_back) {System Libraries};
* Source Code Analysis % \node[process,xshift=0ex,yshift=-2ex] (sl) at (sl_back) {System Libraries};
- Difficult for low-level code, would require hardware knowledge %
- Compilers are source code analysers by nature % \node[process,xshift=0ex,yshift=-0ex,below of=sl] (os_back) {OS};
- Additional tools can help, but this takes more effort % \node[process,xshift=0ex,yshift=-1ex] at (os_back) {OS API};
-> chose a compiler with high analysis standards % \node[process,xshift=0ex,yshift=-2ex] (os) at (os_back) {OS};
* Choice of Compiler: Language Dependent %
- C: Safe C, Cyclone, etc.: define sub language that is analyzable. MEH % \node[process,xshift=0ex,yshift=-0ex,left of=mem, below of=os] (cpu) {CPU};
- Rust: designed to be analyzable. WIN! % \node[process,xshift=0ex,yshift=-0ex,right of=cpu] (mem) {Memory};
* Rust % \node[process,xshift=0ex,yshift=-0ex,right of=mem] (otherhw) {Other HW};
- Can the analyzes be extended to suite OS dev? %
\end{markdown} % \draw [arrow] (ua) -- (sl);
% \draw [arrow] (sl) -- (os);
% \draw [arrow] (os) -- (cpu);
% \draw [arrow] (os) -- (mem);
% \draw [arrow] (os) -- (otherhw);
%
% TODO: improve
%
% \end{tikzpicture}
%
% \begin{markdown}
% # Flow of Reasoning
% * How to mitigate distributed weaknesses
% - Don't distribute vulnerable software
% - Produce less vulnerabilities
% OR
% - Detect vulnerabilities
% * How to prevent vulnerabilities distribution?
% - Human to make less mistakes; NOT VIABLE, see human aspect.
% - Detect them before the \gls{app} is installed; see time aspect
% * How to detect vulnerabilities
% - Write runtime tests for the program
% - Analyze the source code
% * Runtime Tests
% - Runs on every execution, thus wastes \gls{cpu} resources
% - Program needs to handle it
% -> Slow and too late in the software life cycle!
% * Source Code Analysis
% - Difficult for low-level code, would require hardware knowledge
% - Compilers are source code analysers by nature
% - Additional tools can help, but this takes more effort
% -> chose a compiler with high analysis standards
% * Choice of Compiler: Language Dependent
% - C: Safe C, Cyclone, etc.: define sub language that is analyzable. MEH
% - Rust: designed to be analyzable. WIN!
% * Rust
% - Can the analyzes be extended to suite OS dev?
% \end{markdown}

View file

@ -1,5 +1,5 @@
% // vim: set ft=tex: % // vim: set ft=tex:
\chapter{An Introduction To Rust} \chapter{Rust}
\label{rnd::rust} \label{rnd::rust}
As described by the maintainers, \gls{Rust} is a "systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety."\footnote{\url{www.rust-lang.org}}. As described by the maintainers, \gls{Rust} is a "systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety."\footnote{\url{www.rust-lang.org}}.
During early development it had a runtime-dependent garbage collector which has since been dropped from the language, making it a viable candidate for \gls{os} development. During early development it had a runtime-dependent garbage collector which has since been dropped from the language, making it a viable candidate for \gls{os} development.
@ -45,21 +45,59 @@ By analyzing control flow and evaluation of constant expressions, code paths can
It will also allow type checking to be more effective, as the MIR is simpler than the HIR. It will also allow type checking to be more effective, as the MIR is simpler than the HIR.
An example of a potential optimization is given in the borrow checker explanation along with the \cnameref{rnd::rust::feat::own-borrow::mir-improvement}. An example of a potential optimization is given in the borrow checker explanation along with the \cnameref{rnd::rust::feat::own-borrow::mir-improvement}.
% TODO: Tokens \section{Macros And Extensions}
\subsection{Syntax}
In \gls{os} development, macros are often preferred over functions because they are processed at compile time and induce no runtime overhead.
In \gls{Rust}, they are deeply integrated into the language syntax, and are not trivially usable.
In order to extend Rust for specific use-cases in \gls{os} development with macros or other means, the syntax and its extension mechanisms need to be understood. In order to extend Rust for specific use-cases in \gls{os} development with macros or other means, the syntax and its extension mechanisms need to be understood.
The following information has been extracted from the \url{https://danielkeep.github.io/tlborm/book}. In \gls{os} development, macros are often preferred over functions because they are processed at compile time and induce no runtime overhead.
In \gls{Rust}, they are deeply integrated into the language and are not intuitively usable.
This is because they require the developer to write pattern matches on token trees and understand the abstract syntax tree creation.
\paragraph{Tokens Trees} The most comprehensive literature on Rust's Macro system, including a thorough explanation of available language is provided as a digital book in the rustdoc format\footnote{url{https://danielkeep.github.io/tlborm/book}}.
Rust is a C-like language and doesn't look too much different for simple code.
It starts with tokens, which are similar to other languages.
\subsection{Macro Rules}
A simple macro is presented in \cpnameref{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling::macro}, where it is used as a template for interrupt handler definitions.
% \subsubsection{Macro Recursion Limit}
% Macro recursion can be limited via the attribute:
%
% \mintinline{rust}{#![recursion_limit="10"]}
\subsection{Language Extensions Example: Software Fault Isolation}
Language extensions allow the addition of almost arbitrary functionality to the language.
They can be used for the definition of additional analysis rules to extend safety checks.
Developing these is difficult, but the effort is justified under conditions where regular macros are insufficient.
The mechanics of language extensions are beyond the scope of this study, instead an example is presented demonstrating the results one can achieve with them.
\citeauthor{Balasubramanian2017} have achieved an implementation of Information Control Flow (IFC) analysis in \gls{Rust}, which lets the programmer annotate variables with security contexts.\cite{Balasubramanian2017}.
IFC enables the enforcement of security contexts in information flow without hardware support and detects a violation at compile time.
The following buffer implementation is initialized with the first data element it receives.
\begin{minted}[breaklines,highlightlines={6,7}]{rust}
struct Buffer{data: Option <Vec <u8 >>}
impl Buffer {
fn new () -> Buffer {Buffer{data: None }}
fn append (& mut self , mut v: Vec <u8 >) {
match self.data {
None => self.data = Some(v),
Some(ref mut d) => d.append (& mut v) }
} }
\end{minted}
In this sample, variables from two different security contexts are stored in the buffer.
The \code{println!} macro is considered \code{non-secret} and may not read the data since it contain \code{secret} items.
\begin{minted}[breaklines,highlightlines={2-3,5-6,9}]{rust}
let mut buf = Buffer::new();
#[ label(non-secret )] // security annotation for IFC
let nonsec = vec![1,2,3];
#[ label(secret )] // security annotation for IFC
let sec = vec![4,5,6];
buf.append(nonsec);
buf.append(sec); // buf now contains secret data
println!("{:?}" , buf.data ); // ERROR : leaks secret data
\end{minted}
\subsection{Extensions}
\subsubsection{Definition Of Additional Analysis Rules To Extend Safety Checks}
% TODO: Business Logic Checks % TODO: Business Logic Checks
% Examples: % Examples:
% TLB needs to be reset on Task Change % TLB needs to be reset on Task Change
@ -72,19 +110,10 @@ It starts with tokens, which are similar to other languages.
% TODO * Tasks can't access unallocated (physical) memory % TODO * Tasks can't access unallocated (physical) memory
% TODO * Tasks can't access other tasks memory % TODO * Tasks can't access other tasks memory
% \subsection{Compiler Plugins}
\subsection{Macro Rules} % The Rust Unstable Book \url{https://doc.rust-lang.org/unstable-book/language-features/plugin.html}
A rather simple macro is presented in \cpnameref{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling::macro}. % has a section on compiler plugins, which are user-provided libraries that extend the compiler's behavior with new syntax extensions, lint checks, etc.
% This is
\subsubsection{Macro Recursion Limit}
Macro recursion can be limited via the attribute:
\mintinline{rust}{#![recursion_limit="10"]}
\subsection{Compiler Plugins}
The Rust Unstable Book \url{https://doc.rust-lang.org/unstable-book/language-features/plugin.html}
has a section on compiler plugins, which are user-provided libraries that extend the compiler's behavior with new syntax extensions, lint checks, etc.
This is
\subsection{Cargo} \subsection{Cargo}
\glsentrydesc{cargo}. \glsentrydesc{cargo}.
@ -94,7 +123,7 @@ This is
Using \gls{cargo}, arguments for the \gls{llvm} \gls{compiler} can be passed all the way down to by creating the \code{$PROJECT_DIR/.cargo/config} file. Using \gls{cargo}, arguments for the \gls{llvm} \gls{compiler} can be passed all the way down to by creating the \code{$PROJECT_DIR/.cargo/config} file.
The following is an example which has been used to experiment with stack protection in \cref{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space}. The following is an example which has been used to experiment with stack protection in \cref{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space}.
\begin{minted}{yaml} \begin{minted}[breaklines]{yaml}
[build] [build]
rustflags = [ rustflags = [
"-C", "llvm-args=-safe-stack-layout -enable-stackovf-sanitizer -asan-stack -warn-stack-size=1000", "-C", "llvm-args=-safe-stack-layout -enable-stackovf-sanitizer -asan-stack -warn-stack-size=1000",
@ -121,18 +150,18 @@ Standalone tools of \gls{llvm} might not expose the same functionality as the \g
The following sentence is placed here according to the Don't-Repeat-Yourself principle as it would have otherwise been in almost every subsection: The following sentence is placed here according to the Don't-Repeat-Yourself principle as it would have otherwise been in almost every subsection:
Developers unfamiliar with this concept are likely to take a while to get used to it, but safety-gains are well worth the effort. Developers unfamiliar with this concept are likely to take a while to get used to it, but safety-gains are well worth the effort.
\subsection{Memory Management} % \subsection{Memory Management}
- TODO: Static Variables on Stack, handled by compiler % - TODO: Static Variables on Stack, handled by compiler
%
% - TODO: Heap requires implemented allocator
%
% - TODO: BSYS SS17 GITHUB IO Rust Memory Layout - 4
% - TODO: How can memory be dynamically allocated and still safety checked?
- TODO: Heap requires implemented allocator % \subsubsection{Custom Allocators}
% - TODO: mention ralloc by redox
- TODO: BSYS SS17 GITHUB IO Rust Memory Layout - 4 % - TODO: simple allocator by Blog OS
- TODO: How can memory be dynamically allocated and still safety checked? % - TODO: Who owns global 'static variables?
\subsubsection{Custom Allocators}
- TODO: mention ralloc by redox
- TODO: simple allocator by Blog OS
- TODO: Who owns global 'static variables?
\subsection{Ownership And Borrows} \subsection{Ownership And Borrows}
\citeauthor{Beingessner2015} explores the ownership model in relation to some of the weaknesses explained in \cref{context::weaknesses-mem-safety}. \citeauthor{Beingessner2015} explores the ownership model in relation to some of the weaknesses explained in \cref{context::weaknesses-mem-safety}.
@ -175,29 +204,35 @@ In the latter case, the borrow checker would complain.
This is because it does not consider the two constant indices to borrow different items from the vector, but considers the whole vector to be borrowed by the first statement, causing an error for the second borrow attempt of the vector. This is because it does not consider the two constant indices to borrow different items from the vector, but considers the whole vector to be borrowed by the first statement, causing an error for the second borrow attempt of the vector.
\subsection{Static Analyser} \subsection{Static Analyser}
- TODO: How does the Rust's static analysis work, theoretically and practically The static analyser has been studies extensively throughout this part.
- TODO: mention electrolyte, formal verification for Rust Specifically \Cref{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space}, which tests the capability of static detection of obvious stack overflow scenarios.
- TODO: How does static typing help with preventing programming errors
- TODO: explain lints %- TODO: How does the Rust's static analysis work, theoretically and practically
%- TODO: mention electrolyte, formal verification for Rust
%- TODO: How does static typing help with preventing programming errors
%
%- TODO: explain lints
\subsection{Inline Assembly} \subsection{Inline Assembly}
Inline assembly is explained by example in \cref{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling} Inline assembly is explored two examples within this study.
Inside the scheduler to instruct the compiler's register clobbering: \cpnameref{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling},
and in the redirection of the boot task shown in \cpnameref{rnd::imezzos-preemptive-multitasking::tasks-stacks::unsafe::jmp}.
A more formal and helpful tutorial which is suggested, has been found in form of a web article.\footnote{\url{http://embed.rs/articles/2016/arm-inline-assembly-rust/}} A more formal and helpful tutorial which is suggested, has been found in form of a web article.\footnote{\url{http://embed.rs/articles/2016/arm-inline-assembly-rust/}}
\subsection{Lifetimes} % \subsection{Lifetimes}
- TODO: Where are global 'static variables allocated? % Lifetimes were not used intensively
\subsection{Type Safety} \subsection{Type Safety}
- TODO: demonstrate casts
- TODO: demonstrate raw pointers: % - TODO: demonstrate casts
% https://rustbyexample.com/flow_control/match/destructuring/destructure_pointers.html %
% - TODO: demonstrate raw pointers:
% % https://rustbyexample.com/flow_control/match/destructuring/destructure_pointers.html
%
% - TODO: discuss the equivalents of void*?
- TODO: discuss the equivalents of void*? \subsubsection{Single Field Structs}
\subsection{Single Field Structs}
Structs with a single field can be used to wrap a under a different type name, and make it distinguishable for the type system. Structs with a single field can be used to wrap a under a different type name, and make it distinguishable for the type system.
This is different from a type alias, which wouldn't prevent the example situation given below. This is different from a type alias, which wouldn't prevent the example situation given below.
This extended example\footnote{\url{https://aturon.github.io/features/types/newtype.html}} shows one way of preventing the mix-up of common length units. This extended example\footnote{\url{https://aturon.github.io/features/types/newtype.html}} shows one way of preventing the mix-up of common length units.
@ -250,12 +285,8 @@ error[E0308]: mismatched types
- .as_miles() - .as_miles()
\end{minted} \end{minted}
\subsection{Empty Types} \subsubsection{Uninstantiable Types}
\label{rnd::rust::type-safety::empty-types} They can be used to statically prevent certain code paths or mark other impossible conditions in the code.
Empty types are abstract types that can not be instantiated.
\subsubsection{Unreachable Code Paths}
They can be used to statically prevent certain code paths, declaring them impossible.
The simplest example is a function that is defined to never return: The simplest example is a function that is defined to never return:
\begin{minted}[linenos,breaklines]{rust} \begin{minted}[linenos,breaklines]{rust}
@ -297,19 +328,16 @@ This demonstrates that the empty enum cannot be instantiated, and is merely a sy
Rust includes the \code{!} type for this purpose, and the function could've been written as \mintinline{rust}{fn never_returns() -> ! { loop{} }}. Rust includes the \code{!} type for this purpose, and the function could've been written as \mintinline{rust}{fn never_returns() -> ! { loop{} }}.
This pattern can be used in \gls{os} development for the \gls{os}'s function that runs the main loop, and is not supposed to return. This pattern can be used in \gls{os} development for the \gls{os}'s function that runs the main loop, and is not supposed to return.
\subsubsection{In Combination With Traits And PhantomData} \paragraph{In Combination With Traits And PhantomData}
Emtpy enums can be used for more advanced use-cases in combination with traits, as shown in \cref{rnd::existing-os-dev-with-rust::systems::blog-os::mm}, where the lowest level of the page hierarchy is prevented from calling the \code{next_table} method. Emtpy enums can be used for more advanced use-cases in combination with traits, as shown in \cref{rnd::existing-os-dev-with-rust::systems::blog-os::mm}, where the lowest level of the page hierarchy is prevented from calling the \code{next_table()} method.
\subsection{Inner- and Outer Mutability} \subsection{Inner- and Outer Mutability}
Some types in \gls{Rust} provide interior mutability, so that their \emph{value} can be mutated even though they have not been declared using \code{mut}. Some types in \gls{Rust} provide interior mutability, so that their \emph{value} can be mutated even though they have not been declared using \code{mut}.
An example of this is found in with the \code{spin::Mutex} type used in %\cpnameref{}. This study has two usages of types with interior mutability:
\code{AtomicUsize}, used in \cpnameref{code::imezzos-preemptive-multitasking::clock::tick} and \code{spin::Mutex} used in \cpnameref{rnd::example::mutex}.
Other examples which are not covered in this study include \code{Rc}, \code{Arc}, \code{RefCell}. Other types which are not covered in this study include \code{Rc}, \code{Arc}, \code{RefCell}.
\section{Limitations}
* TODO: deadlock example
* TODO: cyclic reference memory leak example
\chapter{Weakness Mitigation And Prevention} \chapter{Weakness Mitigation And Prevention}
\label{rnd::weakness-mitig-prev} \label{rnd::weakness-mitig-prev}
@ -592,14 +620,14 @@ The \gls{os} can then check if the guard page was accessed or if the stack is pe
As this code was extracted from a binary, the estimated stack size must have been calculcated at compile-time. As this code was extracted from a binary, the estimated stack size must have been calculcated at compile-time.
This is fortunate and drives the investigation further if this check could be performed entirely at compile-time. This is fortunate and drives the investigation further if this check could be performed entirely at compile-time.
\paragraph{Compile-Time Prevention} \paragraph{Attempted Compile-Time Prevention}
\label{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space::compile-time} \label{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space::compile-time}
%The compile-time prevention of the stack clash depends on the ability to predict the stack size and its boundaries accurately. %The compile-time prevention of the stack clash depends on the ability to predict the stack size and its boundaries accurately.
%This investigation justifies a separate chapter, please see \cref{rnd::stack-size-estimation}. %This investigation justifies a separate chapter, please see \cref{rnd::stack-size-estimation}.
% %
%\chapter{Compile-Time Stack-Size Estimation} %\chapter{Compile-Time Stack-Size Estimation}
%\label{rnd::stack-size-estimation} %\label{rnd::stack-size-estimation}
By estimating the stack size at compile-time the stack clash -- covered in \cref{rnd::weakness-mitig-prev::stack-protection::stack-clash}) -- and other undesired stack scenarios, could be predicted without running into them. By estimating the stack size at compile-time the stack clash -- covered in \cref{rnd::weakness-mitig-prev::stack-protection::stack-clash} -- and other undesired stack scenarios, could be predicted without running into them.
In theory, this analysis requires a prediction of the worst-case stack growth for each procedure based on source code information. In theory, this analysis requires a prediction of the worst-case stack growth for each procedure based on source code information.
This maximum stack growth size must then be compared to stack size limit, as well as the distance and the size of the guard area; it must be equal or less than all given limits. This maximum stack growth size must then be compared to stack size limit, as well as the distance and the size of the guard area; it must be equal or less than all given limits.
This could effectively prevent the stack from overflowing and from touching or leaping over the guard area. This could effectively prevent the stack from overflowing and from touching or leaping over the guard area.
@ -671,7 +699,7 @@ fn main() {
The highlighted lines in \cref{code::examples::huge-stack-rust} construct a slice on the stack with the size of $8 * 0x100000000 = 0x800000000 = 4,294,967,296$ Bytes (4GiB), which would fill the main memory of any 32-Bit system and should definitely be enough to trigger the configured stack warning. The highlighted lines in \cref{code::examples::huge-stack-rust} construct a slice on the stack with the size of $8 * 0x100000000 = 0x800000000 = 4,294,967,296$ Bytes (4GiB), which would fill the main memory of any 32-Bit system and should definitely be enough to trigger the configured stack warning.
Unexpectedly this program compiled without a warning; Unexpectedly this program compiled without a warning;
It was expected that the \gls{compiler} detects this huge statically allocated stack array, compares it to the configured maximum allowed size and reports the violation. It was expected that the \gls{compiler} detects this huge statically allocated stack slice, compares it to the configured maximum allowed size and reports the violation.
At runtime it crashes with this message: At runtime it crashes with this message:
\begin{minted}{md} \begin{minted}{md}
@ -958,11 +986,11 @@ The main author of Redox OS has become an active contributor to the Rust languag
The biggest achievement from the perspective of this study is the successful integration into Rust's libstd, which happened continuously and cannot be referenced easily. The biggest achievement from the perspective of this study is the successful integration into Rust's libstd, which happened continuously and cannot be referenced easily.
This allows programmers to use Rust with all it's features to develop programs for Redox OS. This allows programmers to use Rust with all it's features to develop programs for Redox OS.
\subsection{Tock OS} % \subsection{Tock OS}
Tock OS is "an embedded operating system designed for running multiple concurrent, mutually distrustful applications on low-memory and low-power microcontrollers."\cite{TockOS} % Tock OS is "an embedded operating system designed for running multiple concurrent, mutually distrustful applications on low-memory and low-power microcontrollers."\cite{TockOS}
%
\subsubsection{Task Model} % \subsubsection{Task Model}
\subsubsection{Memory Management} % \subsubsection{Memory Management}
\subsection{intermezzOS} \subsection{intermezzOS}
"intermezzOS is a teaching operating system, specifically focused on introducing systems programming concepts to experienced developers from other areas of programming."\footnote{\url{https://intermezzos.github.io/}} "intermezzOS is a teaching operating system, specifically focused on introducing systems programming concepts to experienced developers from other areas of programming."\footnote{\url{https://intermezzos.github.io/}}
@ -1059,7 +1087,7 @@ The build process gives an impression of what is required to build an \gls{os} e
\section{Development State} \section{Development State}
The anticipated development of preemptive multitasking has been reached. The anticipated development of preemptive multitasking has been reached.
Tasks are represented by plain \code{fn()} instances. Tasks are represented by plain \code{fn() -> !} instances.
The tasks and the task table are statically defined in the \gls{os} source code. The tasks and the task table are statically defined in the \gls{os} source code.
Task switches are driven by the Programmable-Interrupt-Timer, for which a driver has been implemented. Task switches are driven by the Programmable-Interrupt-Timer, for which a driver has been implemented.
The task scheduler works in a round-robin fashion and detects stack overflows. The task scheduler works in a round-robin fashion and detects stack overflows.
@ -1204,11 +1232,19 @@ impl Clock for Pit {
\end{minted} \end{minted}
The \code{start} method is the first occurrence of \code{unsafe}, which is required to perform raw I/O port access using \code{outb}. The \code{start} method is the first occurrence of \code{unsafe}, which is required to perform raw I/O port access using \code{outb}.
\code{tick} is extremely simple, it uses a method to atomically add one, requesting a specific ordering: \textit{SeqCstr: Like AcqRel with the additional guarantee that all threads see all sequentially consistent operations in the same order}.\footnote{\url{https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html}} \code{tick} is extremely simple, it uses a method to atomically add one, requesting a specific ordering: \textit{SeqCstr: Like AcqRel with the additional guarantee that all threads see all sequentially consistent operations in the same order}.\footnote{\url{https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html}}
This method is called in the \gls{os} timer interrupt handler. This method is called in the \gls{os} timer interrupt handler, as indicated in \cref{code::imezzos-preemptive-multitasking::clock::tick}.
\begin{listing}[ht!]
% TODO: Is the static analysis of hardware specific assembly code possible and useful at all? \begin{minted}[breaklines]{rust}
% TODO: LLVM knows about the target and can potentially give hints about hardware specific instructions let timer = make_idt_entry!(isr32, esf: &mut ExceptionStackFrame, true, {
...
unsafe { CLOCK.tick() };
...
\end{minted}
\caption{Ticking The Clock}
\label{code::imezzos-preemptive-multitasking::clock::tick}
\end{listing}
\FloatBarrier
\section{Timer Interrupt For Scheduling and Dispatching} \section{Timer Interrupt For Scheduling and Dispatching}
\label{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling} \label{rnd::imezzos-preemptive-multitasking::timer-interrupt-scheduling}
@ -1273,25 +1309,62 @@ Thanks to the pull-request described in \cpnameref{rnd::existing-os-dev-with-rus
It enables the proper handling of the first argument, and in combination with the \emph{clobber} registers shown in \cref{code::imezzos::ir-handler-macro} line 11, enables the compiler to generate a functoin pro- and epilogue to automatically \code{PUSH/POP} all named registers from the stack. It enables the proper handling of the first argument, and in combination with the \emph{clobber} registers shown in \cref{code::imezzos::ir-handler-macro} line 11, enables the compiler to generate a functoin pro- and epilogue to automatically \code{PUSH/POP} all named registers from the stack.
As a result, the inline assembly string provided by the programmer is empty, which alleviates the necessatiy of \code{unsafe}. As a result, the inline assembly string provided by the programmer is empty, which alleviates the necessatiy of \code{unsafe}.
\paragraph{Inline Assembly}
Further, the inline assembly is interesting.
\paragraph{Inline Assembly}
\begin{minted}[breaklines]{rust}
let timer = make_idt_entry!(isr32, esf: &mut ExceptionStackFrame, true, {
...
unsafe { CLOCK.tick() };
...
\end{minted}
TODO
\section{Tasks and Stacks} \section{Tasks and Stacks}
\label{rnd::imezzos-preemptive-multitasking::tasks-stacks} \label{rnd::imezzos-preemptive-multitasking::tasks-stacks}
The implementation of the tasks has been kept straight forward, using static variables. The implementation of the tasks has been kept straight forward, using global static variables and simple struct types instead of traits.
Tasks are defined globally with the simple \code{fn() -> !} type.
\subsection{Initial Unsafe JMP}
\label{rnd::imezzos-preemptive-multitasking::tasks-stacks::unsafe::jmp}
To redirect the codepath of the boot task, the solution in \cref{code::imezzos::jump-to-task0} has been implemented.
The function \code{schedule_and_dispatch()} is called from the \gls{os} \code{main() -> !} method, which will never return.
It shows the combination of Rust high-level code, which leverages the highlighted lifetime to seamlessly lock and drop the \code{TSI} variable.
The numbered circle show another interesting usage of the language.
The variables are declare immutable (no \code{mut}), and are initialized within the nested scope.
Another write to these variables would result in a compilation error.
They end up being passed to the inline assembly, where they are further processed.
\begin{listing}[ht!]
\begin{minted}[escapeinside=??,breaklines,highlightlines={7-14,16-30}]{rust}
#[naked]
fn schedule_and_dispatch() {
let rbp; ?\tikzmarkcircle{1}?
let rsp; ?\tikzmarkcircle{2}?
let rip; ?\tikzmarkcircle{3}?
{
let tsi = TSI.lock();
let te = tsi.get_current_task();
rbp = te.stack.top; ?\tikzmarkcircle{1}?
rsp = te.esf.stack_pointer; ?\tikzmarkcircle{2}?
rip = te.esf.instruction_pointer; ?\tikzmarkcircle{3}?
};
unsafe {
asm!("
mov rbp, $0 "?\tikzmarkcircle{1}?"
jmp $1 "?\tikzmarkcircle{2}?"
"
: // output operands
: // input operands
"r"(rbp) ?\tikzmarkcircle{1}?
"r"(rip) ?\tikzmarkcircle{3}?
"{rsp}="(rsp) ?\tikzmarkcircle{2}?
: // clobbers
: // options
"intel" "volatile"
);
};
}
\end{minted}
\caption{intermezzOS: Initial Jump To Task 0}
\label{code::imezzos::jump-to-task0}
\end{listing}
\subsection{Declaration and Intantiation} \subsection{Declaration and Intantiation}
\paragraph{Global Stacks}
\label{rnd::imezzos-preemptive-multitasking::tasks-stacks::dni} \label{rnd::imezzos-preemptive-multitasking::tasks-stacks::dni}
\Cref{code::imezzos::stack-and-tasks-1} defines a \code{Stack} with a top and a bottom address based which are offset by a constant. \Cref{code::imezzos::stack-and-tasks-1} defines a \code{Stack} with a top and a bottom address based which are offset by a constant.
Subsequent stacks grow the multiplier by 10, which keeps space between the stacks. Subsequent stacks grow the multiplier by 10, which keeps space between the stacks.
@ -1310,7 +1383,8 @@ const TASK0_STACK: Stack = Stack {
\label{code::imezzos::stack-and-tasks-1} \label{code::imezzos::stack-and-tasks-1}
\end{listing} \end{listing}
\Cref{code::imezzos::stack-and-tasks-2} defines a \code{TaskEntry} in a static array of the same. \paragraph{Global TaskList}
\Cref{code::imezzos::stack-and-tasks-2} defines a \code{TaskEntry} in a static slice of the same.
The highlighted lines are unique to each task. The highlighted lines are unique to each task.
In the given order, they represent their first instruction, their initial top of stack, and their initial set of \gls{cpu} registers. In the given order, they represent their first instruction, their initial top of stack, and their initial set of \gls{cpu} registers.
Except for the instruction pointer, these variables have their own type and cannot easily be mixed up. Except for the instruction pointer, these variables have their own type and cannot easily be mixed up.
@ -1341,7 +1415,9 @@ Except for the instruction pointer, these variables have their own type and cann
\label{code::imezzos::stack-and-tasks-2} \label{code::imezzos::stack-and-tasks-2}
\end{listing} \end{listing}
\Cref{code::imezzos::stack-and-tasks-3} wraps this array by a \code{Mutex}, which is returned by the expression and stored as a \code{lazy_static} reference as explained in the previous section. \paragraph{Mutex With Interior Mutability}
\label{rnd::example::mutex}
\Cref{code::imezzos::stack-and-tasks-3} wraps this slice in a \code{spin::Mutex}, which is returned by the expression and stored as a \code{lazy_static} reference as explained in the previous section.
The \code{Mutex} type is interesting, as it provides \emph{interior mutability}. The \code{Mutex} type is interesting, as it provides \emph{interior mutability}.
This explains how the tasklist can be mutated at runtime, even though it is not declared as \code{mut}. This explains how the tasklist can be mutated at runtime, even though it is not declared as \code{mut}.
@ -1363,12 +1439,91 @@ static ref TSI: Mutex<tasks::TaskStateInformation> = {
\end{listing} \end{listing}
\subsection{Preemptive Task Switches} \subsection{Preemptive Task Switches}
What follows in \cref{code::imezzos::taskswitch-1,} is the most low-level part in this study, the actual context switch within the interrupt handler.
It does these things: (* marks unsafe actions)
\begin{enumerate}
\item * Read the \gls{sf} Base-Pointer (line 2)
\item Calculate the offset to the Registers on the \gls{sf}
\item * Cast this address to a type that contains all registers (line 5-6)
\item ? Pass the Exception\gls{sf} and the registers along to \code{manage_tasks}
\item Acknowledge the interrupt
\end{enumerate}
Looking at this code in retrospection suggests that \code{manage_tasks} could be marked as \code{unsafe} too because it does \code{unsafe} things inside.
Through the values it consumed, it is able to directly modify the stack contents.
Arguably it is done through modeled types, but it is not the way the \gls{stack} was designed to be used by a programmer.
\begin{listing}[ht!]
\begin{minted}[breaklines,highlightlines={2,5-6},linenos]{rust}
let timer = make_idt_entry!(isr32, esf: &mut ExceptionStackFrame, true, {
let rbp_on_stack: *mut usize = unsafe { (get_register!("rbp") as *mut usize) };
let rax_offset = 1 - (mem::size_of::<tasks::TaskRegisters>() as isize / 8);
let rax_on_stack: *mut usize = unsafe { rbp_on_stack.offset(rax_offset) };
let registers_on_stack: &mut tasks::TaskRegisters =
unsafe { mem::transmute::<*mut usize, &mut tasks::TaskRegisters>(rax_on_stack) };
...
manage_tasks(esf, registers_on_stack);
pic::eoi_for(32);
};
\end{minted}
\caption{intermezzOS: Taskswitch - 1}
\label{code::imezzos::taskswitch-1}
\end{listing}
\subsection{Task Definitions} \subsection{Task Definitions}
The task definitions are straight forward as explained.
\paragraph{Idle Task}
\Cref{code::imezzos::idel-task} shows the system's idle task, which infinitively calls \code{hlt()} after finishing the boot process.
\begin{listing}[ht!]
\begin{minted}[breaklines,highlightlines={5}]{rust}
#[deny(unsafe_code)]
fn task0() -> !{
unsafe { CLOCK.start() };
kprintln!(CONTEXT,
"System clock set up. Frequency: {} / Resolution: {}ns",
CLOCK.frequency,
CLOCK.resolution);
kprintln!(CONTEXT,
"Kernel initialized, final step: enabling interrupts");
CONTEXT.idt.enable_interrupts();
loop {
hlt();
}
}
\end{minted}
\caption{intermezzOS: Idle Task}
\label{code::imezzos::idel-task}
\end{listing}
\section{Safety Concerns}
\section{Safety}
\subsection{Protecting Static Resources} \subsection{Protecting Static Resources}
TODO With this straight forward task implementation any task has access to the globally defined reference variables which hold the system state.
For this reason, the \code{Clock} trait makes use of the \code{unsafe} keyword.
As seen in this code snippet, the tasks can be prevented from accessing any \code{unsafe} by adding the appropriate annotation.
Of course, this does not make sense for the system's idle task, but it is suitable for an example.
\begin{listing}[ht!]
\begin{minted}[breaklines,highlightlines={5}]{rust}
#[deny(unsafe_code)]
fn task0() {
unsafe { CLOCK.start() };
...
}
\end{minted}
\end{listing}
This causes the compiler to abort compilation with the following error:
\begin{minted}{md} \begin{minted}{md}
error: usage of an `unsafe` block error: usage of an `unsafe` block
--> src/main.rs:499:5 --> src/main.rs:499:5
@ -1383,12 +1538,34 @@ note: lint level defined here
| |
\end{minted} \end{minted}
\subsection{Risk Of Stack-Overflow} \subsection{Vulnerable To In-Kernel Stack Overflow}
TODO As investigated in \cpnameref{rnd::weakness-mitig-prev::stack-protection::stack-clash::user-space::compile-time}, \gls{Rust} does not detect stack overflows at compile-time.
- TODO: reference stack protection Without a paging implementation that sets up a guard area for each task, there is no guarantees on memory-safety within this \gls{os}.
Give a practical example what this could look like with an extension attribute
\chapter{Result Summary} The trivial runtime mitigation is to employ a boundary check in the task management function, as shown in \cref{code::imezzos:stack-of-detect}, and in addition place the task's stacks far apart.
- TODO If the stack pointer of the preempted task is not within it's known stack boundaries, the task is blocked from further scheduling.
TODO This solution leaves each task enough time to overflow it's stack by enough space to reach a memory area of another task or the \gls{os}.
This may happen within one scheduling period before it can be detected.
\begin{listing}[ht!]
\begin{minted}[breaklines,highlightlines={5}]{rust}
fn manage_tasks(esf: &mut ExceptionStackFrame, registers: &mut tasks::TaskRegisters) {
...
if let Some(mut tsi) = TSI.try_lock() {
...
if !tsi.get_current_task().stack.contains(esf.stack_pointer) {
kprintln_try!(CONTEXT,
"Stack overflow in task {}!\nStack: {:x}\nESF: {:x}\nREGS: {:x}",
tsi.current_task,
tsi.get_current_task().stack,
esf,
registers);
tsi.get_current_task_mut().blocked = true;
}
}
...
}
\end{minted}
\caption{intermezzOS: Runtime Stack Overflow Detection}
\label{code::imezzos:stack-of-detect}
\end{listing}

View file

@ -8,7 +8,7 @@
\usepackage{blindtext,color,fancyhdr} \usepackage{blindtext,color,fancyhdr}
\usepackage{geometry} \usepackage{geometry}
\geometry{a4paper, top=25mm, left=30mm, right=35mm, bottom=35mm, headsep=10mm, footskip=12mm} \geometry{a4paper, top=25mm, left=35mm, right=30mm, bottom=35mm, headsep=10mm, footskip=12mm}
\usepackage{multirow,tabularx,tabu} \usepackage{multirow,tabularx,tabu}
\usepackage{booktabs} \usepackage{booktabs}