Update app

This commit is contained in:
brunodev85 committed 2024-03-27 14:24:58 -03:00
1 parent 8b1219c9ec
commit 0801cf66d5
309 files changed
+81708 -947

No files matched your search

+8 -4
View File
@@ -1,21 +1,25 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
compileSdk 30
buildToolsVersion '30.0.3'
defaultConfig {
applicationId 'com.winlator'
minSdkVersion 26
targetSdkVersion 28
versionCode 11
versionName "5.0"
versionCode 12
versionName "6.0"
}
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a'
}
}
}
@@ -25,7 +29,7 @@ android {
externalNativeBuild {
cmake {
version '3.10.2'
version '3.22.1'
path 'src/main/cpp/CMakeLists.txt'
}
}
+1
View File
@@ -0,0 +1 @@
*.txz filter=lfs diff=lfs merge=lfs -text
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{"system32":["winedevice.exe","explorer.exe","iexplore.exe","notepad.exe","msxml.dll","msxml2.dll","msxml3.dll","msxml4.dll","msxml6.dll","shdocvw.dll","acledit.dll","aclui.dll","activeds.dll","activeds.tlb","actxprxy.dll","adsldp.dll","adsldpc.dll","advapi32.dll","advpack.dll","amsi.dll","amstream.dll","apisetschema.dll","apphelp.dll","appwiz.cpl","arp.exe","atl.dll","atl100.dll","atl110.dll","atl80.dll","atl90.dll","atlthunk.dll","atmlib.dll","attrib.exe","authz.dll","avicap32.dll","avifil32.dll","avrt.dll","bcrypt.dll","bluetoothapis.dll","browseui.dll","bthprops.cpl","cabarc.exe","cabinet.dll","cacls.exe","capi2032.dll","cards.dll","cdosys.dll","certutil.exe","cfgmgr32.dll","chcp.com","clock.exe","clusapi.dll","cmd.exe","combase.dll","comcat.dll","comctl32.dll","comdlg32.dll","compstui.dll","comsvcs.dll","concrt140.dll","conhost.exe","connect.dll","control.exe","credui.dll","crtdll.dll","crypt32.dll","cryptdlg.dll","cryptdll.dll","cryptext.dll","cryptnet.dll","cryptowinrt.dll","cryptsp.dll","cryptui.dll","cscript.exe","ctapi32.dll","ctl3d32.dll","d2d1.dll","d3d10.dll","d3d10core.dll","d3d10_1.dll","d3d11.dll","d3d12.dll","d3d8.dll","d3d8thk.dll","d3d9.dll","d3dcompiler_33.dll","d3dcompiler_34.dll","d3dcompiler_35.dll","d3dcompiler_36.dll","d3dcompiler_37.dll","d3dcompiler_38.dll","d3dcompiler_39.dll","d3dcompiler_40.dll","d3dcompiler_41.dll","d3dcompiler_42.dll","d3dcompiler_43.dll","d3dcompiler_46.dll","d3dcompiler_47.dll","d3dim.dll","d3dim700.dll","d3drm.dll","d3dx10_33.dll","d3dx10_34.dll","d3dx10_35.dll","d3dx10_36.dll","d3dx10_37.dll","d3dx10_38.dll","d3dx10_39.dll","d3dx10_40.dll","d3dx10_41.dll","d3dx10_42.dll","d3dx10_43.dll","d3dx11_42.dll","d3dx11_43.dll","d3dx9_24.dll","d3dx9_25.dll","d3dx9_26.dll","d3dx9_27.dll","d3dx9_28.dll","d3dx9_29.dll","d3dx9_30.dll","d3dx9_31.dll","d3dx9_32.dll","d3dx9_33.dll","d3dx9_34.dll","d3dx9_35.dll","d3dx9_36.dll","d3dx9_37.dll","d3dx9_38.dll","d3dx9_39.dll","d3dx9_40.dll","d3dx9_41.dll","d3dx9_42.dll","d3dx9_43.dll","d3dxof.dll","davclnt.dll","dbgeng.dll","dbghelp.dll","dciman32.dll","dcomp.dll","ddraw.dll","ddrawex.dll","devenum.dll","dhcpcsvc.dll","dhcpcsvc6.dll","dhtmled.ocx","difxapi.dll","dinput.dll","dinput8.dll","directmanipulation.dll","dism.exe","dispex.dll","dllhost.exe","dmband.dll","dmcompos.dll","dmime.dll","dmloader.dll","dmscript.dll","dmstyle.dll","dmsynth.dll","dmusic.dll","dmusic32.dll","dnsapi.dll","dplay.dll","dplaysvr.exe","dplayx.dll","dpnaddr.dll","dpnet.dll","dpnhpast.dll","dpnhupnp.dll","dpnlobby.dll","dpnsvr.exe","dpvoice.dll","dpvsetup.exe","dpwsockx.dll","drmclien.dll","dsdmo.dll","dsound.dll","dsquery.dll","dssenh.dll","dsuiext.dll","dswave.dll","dwmapi.dll","dwrite.dll","dx8vb.dll","dxdiag.exe","dxdiagn.dll","dxgi.dll","esent.dll","expand.exe","fc.exe","glu32.dll","hnetcfg.dll","icmp.dll","ieproxy.dll","initpki.dll","inkobj.dll","joy.cpl","jsproxy.dll","kerberos.dll","lz32.dll","mciavi32.dll","mf.dll","mfmediaengine.dll","mfreadwrite.dll","mgmtapi.dll","mlang.dll","mprapi.dll","msasn1.dll","msctf.dll","msgsm32.acm","msimtf.dll","msinfo32.exe","mspatcha.dll","msrle32.dll","msv1_0.dll","msvcp140.dll","msvcp140_1.dll","msvcr71.dll","msvcrt20.dll","msvcrtd.dll","msvfw32.dll","net.exe","ntdsapi.dll","odbc32.dll","ole32.dll","oleacc.dll","oleview.exe","opengl32.dll","pdh.dll","prntvpt.dll","psapi.dll","qasf.dll","reg.exe","regini.exe","rpcrt4.dll","rpcss.exe","rsaenh.dll","sc.exe","scrobj.dll","scrrun.dll","secur32.dll","sensapi.dll","serialui.dll","sfc_os.dll","slbcsp.dll","srvcli.dll","systeminfo.exe","tbs.dll","tdh.dll","ucrtbase.dll","uianimation.dll","uiribbon.dll","vcomp100.dll","vcomp140.dll","version.dll","wdscore.dll","websocket.dll","wevtapi.dll","win32u.dll","windows.networking.dll","wineboot.exe","winecfg.exe","winedbg.exe","winefile.exe","winemapi.dll","winemenubuilder.exe","winmm.dll","winusb.dll","winver.exe","wlanapi.dll","wlanui.dll","wmasf.dll","wow64.dll","wow64win.dll","wpcap.dll","wsdapi.dll","wshom.ocx","wsnmp32.dll","wuapi.dll","x3daudio1_0.dll","x3daudio1_4.dll","x3daudio1_6.dll","xactengine2_0.dll","xactengine2_4.dll","xactengine2_9.dll","xapofx1_2.dll","dxtrans.dll","dxva2.dll","explorerframe.dll","extrac32.exe","findstr.exe","fwpuclnt.dll","gamingtcui.dll","gpkcsp.dll","hhctrl.ocx","hlink.dll","ieframe.dll","imaadp32.acm","imagehlp.dll","ipconfig.exe","iphlpapi.dll","iprop.dll","irprops.cpl","kernel32.dll","localspl.dll","mapistub.dll","mcicda.dll","mciqtz32.dll","mciseq.dll","mferror.dll","mpr.dll","msctfmonitor.dll","msdelta.dll","msg711.acm","msident.dll","msisip.dll","msports.dll","msscript.ocx","mssip32.dll","msvcm80.dll","msvcp140_2.dll","msvcp140_atomic_wait.dll","msvcp60.dll","msvcp90.dll","msvcp_win.dll","msvcr100.dll","msvcr80.dll","msvcr90.dll","msvidc32.dll","mswsock.dll","ncrypt.dll","netprofm.dll","netstat.exe","normaliz.dll","ntprint.dll","odbcbcp.dll","odbccp32.dll","oleaut32.dll","olesvr32.dll","pidgen.dll","ping.exe","printui.dll","qcap.dll","qmgrprxy.dll","qwave.dll","rasapi32.dll","regapiLine truncated
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ffbd6b8bb2d8df316038c1ffbe6a882b620ac5a87c078e3272d8299d79de1b64
size 167540620
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,5 +3,6 @@
"directsound" : ["dsound"],
"directmusic" : ["dmband", "dmcompos", "dmime", "dmloader", "dmscript", "dmstyle", "dmsynth", "dmusic", "dmusic32", "dswave"],
"directshow" : ["amstream", "qasf", "qcap", "qdvd", "qedit", "quartz"],
"directplay" : ["dplaysvr.exe", "dplayx", "dpmodemx", "dpnet", "dpnhpast", "dpnhupnp", "dpnsvr.exe", "dpwsockx"]
"directplay" : ["dplaysvr.exe", "dplayx", "dpmodemx", "dpnet", "dpnhpast", "dpnhupnp", "dpnsvr.exe", "dpwsockx"],
"vcrun2010" : ["msvcp100", "msvcr100", "vcomp100", "atl100"]
}
+10 -2
View File
@@ -2,6 +2,10 @@
{
"name" : "Programs",
"children" : [
{
"name" : "7-Zip",
"path" : "Z:/opt/apps/7-Zip/7zFM.exe"
},
{
"name" : "Internet Explorer",
"path" : "C:/windows/system32/iexplore.exe"
@@ -12,7 +16,8 @@
},
{
"name" : "Wordpad",
"path" : "C:/windows/system32/write.exe"
"path" : "C:/windows/system32/write.exe",
"iconLocation" : "Z:/opt/wine/lib/wine/i386-windows/wordpad.exe"
},
{
"name" : "Games",
@@ -46,7 +51,10 @@
},
{
"name" : "Wine Mono Installer",
"path" : "Z:/opt/resources/winemono.bat"
"path" : "C:/windows/system32/wineboot.exe",
"cmdArgs" : "-u",
"iconLocation" : "shell32.dll",
"iconIndex" : -25
},
{
"name" : "Wine Configuration",
+5 -2
View File
@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.10.2)
cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-unused-function")
set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration")
add_subdirectory(proot)
add_subdirectory(virglrenderer)
add_library(winlator SHARED
drawable.c
+4 -4
View File
@@ -108,10 +108,10 @@ Java_com_winlator_xserver_Drawable_copyArea(JNIEnv *env, jclass obj, jshort srcX
JNIEXPORT void JNICALL
Java_com_winlator_xserver_Drawable_copyAreaOp(JNIEnv *env, jclass obj, jshort srcX,
jshort srcY, jshort dstX, jshort dstY,
jshort width, jshort height, jshort srcStride,
jshort dstStride, jobject srcData,
jobject dstData, int gcFunction) {
jshort srcY, jshort dstX, jshort dstY,
jshort width, jshort height, jshort srcStride,
jshort dstStride, jobject srcData,
jobject dstData, int gcFunction) {
uint8_t *srcDataAddr = (*env)->GetDirectBufferAddress(env, srcData);
uint8_t *dstDataAddr = (*env)->GetDirectBufferAddress(env, dstData);
-1
View File
@@ -90,7 +90,6 @@ Java_com_winlator_renderer_GPUImage_lockHardwareBuffer(JNIEnv *env, jclass obj,
JNIEXPORT void JNICALL
Java_com_winlator_renderer_GPUImage_destroyImageKHR(JNIEnv *env, jclass obj, jlong imageKHRPtr) {
EGLImageKHR imageKHR = (EGLImageKHR)imageKHRPtr;
if (imageKHR) {
EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+50
View File
@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.22.1)
project(PRoot)
set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration")
add_library(talloc STATIC
talloc/talloc.c)
include_directories(src
talloc)
add_executable(libproot.so
src/cli/cli.c
src/cli/proot.c
src/cli/note.c
src/execve/enter.c
src/execve/exit.c
src/execve/shebang.c
src/execve/elf.c
src/execve/auxv.c
src/execve/aoxp.c
src/path/binding.c
src/path/glue.c
src/path/canon.c
src/path/path.c
src/path/proc.c
src/path/temp.c
src/syscall/seccomp.c
src/syscall/syscall.c
src/syscall/chain.c
src/syscall/enter.c
src/syscall/exit.c
src/syscall/sysnum.c
src/syscall/socket.c
src/syscall/heap.c
src/syscall/rlimit.c
src/tracee/tracee.c
src/tracee/mem.c
src/tracee/reg.c
src/tracee/event.c
src/tracee/seccomp.c
src/ptrace/ptrace.c
src/ptrace/wait.c)
target_link_libraries(libproot.so
talloc)
add_library(proot-loader SHARED
src/loader/loader.c)
+372
View File
@@ -0,0 +1,372 @@
The copyright holder for PRoot and CARE is STMicroelectronics, these
tools are developed in the Compilation Expertise Center by:
Cédric VINCENT <cedric.vincent@st.com>
Original author, maintainer.
Rémi DURAFFORT <remi.duraffort@st.com>
Improved the -0 feature, many experiments, bug reports, fixes, and articles.
Christophe GUILLON <christophe.guillon@st.com>
Improved CARE, plugin experiments, bug reports, and fixes.
Yves JANIN <yves.janin@st.com>
Support for the i386 architecture, many experiments and bug reports.
Antoine MOYNAULT <antoine.moynault@st.com>
Many experiments and bug reports.
Claire ROBINE <claire.robine@st.com>
Many bug fixes.
Clément BAZIN <clement@bazin-fr.org>
Plugins interface, dependency tracker plugin, and bug reports.
Christian BERTIN <christian.bertin@st.com>
Valuable support.
Denis FERRANTI <denis.ferranti@st.com>
Valuable support.
Paul GHALEB <paul.ghaleb@st.com>
User manual review.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+104
View File
@@ -0,0 +1,104 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef ARCH_H
#define ARCH_H
#ifndef NO_LIBC_HEADER
#include <sys/ptrace.h> /* linux.git:c0a3a20b */
#include <linux/audit.h> /* AUDIT_ARCH_*, */
#endif
typedef unsigned long word_t;
typedef unsigned char byte_t;
#define SYSCALL_AVOIDER ((word_t) -1)
#define SYSTRAP_NUM SYSARG_NUM
#define STACK_ALIGNMENT 16
#if defined(__ARM_EABI__)
#define ARCH_ARM_EABI 1
#elif defined(__aarch64__)
#define ARCH_ARM64 1
#elif defined(__arm__)
#error "Only EABI is currently supported for ARM"
#else
#error "Unsupported architecture"
#endif
/* Architecture specific definitions. */
#if defined(ARCH_ARM_EABI)
#define SYSNUMS_HEADER1 "syscall/sysnums-arm.h"
#define SYSNUMS_ABI1 sysnums_arm
#define SYSTRAP_SIZE 4
#define SECCOMP_ARCHS { { .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_DEFAULT } } }
#define user_regs_struct user_regs
#define HOST_ELF_MACHINE {40, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_UID_32 0
#define OFFSETOF_STAT_GID_32 0
#define EM_ARM 40
#define LOADER_ADDRESS 0x20000000
#define EXEC_PIC_ADDRESS 0x0f000000
#define INTERP_PIC_ADDRESS 0x1f000000
#elif defined(ARCH_ARM64)
#define SYSNUMS_HEADER1 "syscall/sysnums-arm64.h"
#define SYSNUMS_HEADER2 "syscall/sysnums-arm.h"
#define SYSNUMS_ABI1 sysnums_arm64
#define SYSNUMS_ABI2 sysnums_arm
#define SYSTRAP_SIZE 4
#ifndef AUDIT_ARCH_AARCH64
#define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE)
#endif
#define SECCOMP_ARCHS { \
{ .value = AUDIT_ARCH_AARCH64, .nb_abis = 1, .abis = { ABI_DEFAULT } }, \
{ .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_2 } }, \
}
#define HOST_ELF_MACHINE {183, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_UID_32 24
#define OFFSETOF_STAT_GID_32 28
#define LOADER_ADDRESS 0x2000000000
#define EXEC_PIC_ADDRESS 0x3000000000
#define INTERP_PIC_ADDRESS 0x3f00000000
#define HAS_LOADER_32BIT true
#define EXEC_PIC_ADDRESS_32 0x0f000000
#define INTERP_PIC_ADDRESS_32 0x1f000000
#endif
#endif /* ARCH_H */
+32
View File
@@ -0,0 +1,32 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef ATTRIBUTE_H
#define ATTRIBUTE_H
#define UNUSED __attribute__((unused))
#define FORMAT(a, b, c) __attribute__ ((format (a, b, c)))
#define DONT_INSTRUMENT __attribute__((no_instrument_function))
#define PACKED __attribute__((packed))
#define WEAK __attribute__((weak))
#endif /* ATTRIBUTE_H */
+444
View File
@@ -0,0 +1,444 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stdio.h> /* printf(3), */
#include <stdbool.h> /* bool, true, false, */
#include <linux/limits.h> /* ARG_MAX, PATH_MAX, */
#include <string.h> /* str*(3), basename(3), */
#include <talloc.h> /* talloc*, */
#include <stdlib.h> /* exit(3), EXIT_*, strtol(3), {g,s}etenv(3), */
#include <assert.h> /* assert(3), */
#include <sys/types.h> /* getpid(2), */
#include <unistd.h> /* getpid(2), */
#include <errno.h> /* errno(3), */
#include <limits.h> /* INT_MAX, */
#include "cli/cli.h"
#include "cli/note.h"
#include "tracee/tracee.h"
#include "tracee/event.h"
#include "path/binding.h"
#include "path/canon.h"
#include "path/path.h"
/**
* Print a (@detailed) usage of PRoot.
*/
void print_usage(Tracee *tracee, const Cli *cli, bool detailed)
{
const char *current_class = "none";
const Option *options;
size_t i, j;
#define DETAIL(a) if (detailed) a
DETAIL(printf("%s %s: %s.\n\n", cli->name, cli->version, cli->subtitle));
printf("Usage:\n %s\n", cli->synopsis);
DETAIL(printf("\n"));
options = cli->options;
for (i = 0; options[i].class != NULL; i++) {
for (j = 0; ; j++) {
const Argument *argument = &(options[i].arguments[j]);
if (!argument->name || (!detailed && j != 0)) {
DETAIL(printf("\n"));
printf("\t%s\n", options[i].description);
if (detailed) {
if (options[i].detail[0] != '\0')
printf("\n%s\n\n", options[i].detail);
else
printf("\n");
}
break;
}
if (strcmp(options[i].class, current_class) != 0) {
current_class = options[i].class;
printf("\n%s:\n", current_class);
}
if (j == 0)
printf(" %s", argument->name);
else
printf(", %s", argument->name);
if (argument->separator != '\0')
printf("%c*%s*", argument->separator, argument->value);
else if (!detailed)
printf("\t");
}
}
if (detailed)
printf("%s\n", cli->colophon);
}
/**
* Print the version of PRoot.
*/
void print_version(const Cli *cli)
{
printf("%s %s\n\n", cli->logo, cli->version);
}
static void print_execve_help(const Tracee *tracee, const char *argv0, int status)
{
note(tracee, ERROR, SYSTEM, "execve(\"%s\")", argv0);
note(tracee, INFO, USER, "possible causes:\n"
" * the program is a script but its interpreter (eg. /bin/sh) was not found;\n"
" * the program is an ELF but its interpreter (eg. ld-linux.so) was not found;\n"
" * the loader was not found or doesn't work.");
}
static void print_error_separator(const Tracee *tracee, const Argument *argument)
{
if (argument->separator == '\0')
note(tracee, ERROR, USER, "option '%s' expects no value.", argument->name);
else
note(tracee, ERROR, USER, "option '%s' and its value must be separated by '%c'.",
argument->name, argument->separator);
}
static void print_argv(const Tracee *tracee, const char *prompt, char *const argv[])
{
char string[ARG_MAX] = "";
size_t i;
if (!argv)
return;
#define APPEND(post) \
do { \
ssize_t length = sizeof(string) - (strlen(string) + strlen(post)); \
if (length <= 0) \
return; \
strncat(string, post, length); \
} while (0)
APPEND(prompt);
APPEND(" =");
for (i = 0; argv[i] != NULL; i++) {
APPEND(" ");
APPEND(argv[i]);
}
string[sizeof(string) - 1] = '\0';
#undef APPEND
note(tracee, INFO, USER, "%s", string);
}
static void print_config(Tracee *tracee, char *const argv[])
{
assert(tracee != NULL);
if (tracee->verbose <= 0)
return;
if (tracee->glue)
note(tracee, INFO, USER, "glue rootfs = %s", tracee->glue);
note(tracee, INFO, USER, "exe = %s", tracee->exe);
print_argv(tracee, "argv", argv);
note(tracee, INFO, USER, "initial cwd = %s", tracee->fs->cwd);
note(tracee, INFO, USER, "verbose level = %d", tracee->verbose);
}
/**
* Initialize @tracee's current working directory. This function
* returns -1 if an error occurred, otherwise 0.
*/
static int initialize_cwd(Tracee *tracee)
{
char path2[PATH_MAX];
char path[PATH_MAX];
int status;
/* Compute the base directory. */
if (tracee->fs->cwd[0] != '/') {
strcpy(path, "/");
}
else
strcpy(path, tracee->fs->cwd);
/* The ending "." ensures canonicalize() will report an error
* if tracee->fs->cwd does not exist or if it is not a
* directory. */
join_paths(path2, path, ".");
/* Initiate state for canonicalization. */
strcpy(path, "/");
status = canonicalize(tracee, path2, true, path, 0);
if (status < 0) {
note(tracee, WARNING, USER, "can't chdir(\"%s\") in the guest rootfs: %s",
path2, strerror(-status));
note(tracee, INFO, USER, "default working directory is now \"/\"");
strcpy(path, "/");
}
chop_finality(path);
/* Replace with the canonicalized working directory. */
TALLOC_FREE(tracee->fs->cwd);
tracee->fs->cwd = talloc_strdup(tracee->fs, path);
if (tracee->fs->cwd == NULL)
return -1;
talloc_set_name_const(tracee->fs->cwd, "$cwd");
/* Keep this special environment variable consistent. */
setenv("PWD", path, 1);
return 0;
}
/**
* Initialize @tracee->exe from @exe, i.e. canonicalize it from a
* guest point-of-view.
*/
static int initialize_exe(Tracee *tracee, const char *exe)
{
char path[PATH_MAX];
int status;
status = which(tracee, tracee->reconf.paths, path, exe ?: "/bin/sh");
if (status < 0)
return -1;
status = detranslate_path(tracee, path, NULL);
if (status < 0)
return -1;
tracee->exe = talloc_strdup(tracee, path);
if (tracee->exe == NULL)
return -1;
talloc_set_name_const(tracee->exe, "$exe");
return 0;
}
/**
* Configure @tracee according to the command-line arguments stored in
* @argv[]. This function returns the index in @argv[] of the command
* to launch, otherwise -1 if an error occured.
*/
static int parse_config(Tracee *tracee, size_t argc, char *const argv[])
{
option_handler_t handler = NULL;
const Option *options;
const Cli *cli = NULL;
size_t argc_offset;
size_t i, j, k;
int status;
/* Unknown tool name? Default to PRoot. */
if (cli == NULL)
cli = get_proot_cli(tracee->ctx);
if (argc == 1) {
print_usage(tracee, cli, false);
return -1;
}
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
/* The current argument is the value of a short option. */
if (handler != NULL) {
status = handler(tracee, cli, arg);
if (status < 0)
return -1;
handler = NULL;
continue;
}
if (arg[0] != '-')
break; /* End of PRoot options. */
options = cli->options;
for (j = 0; options[j].class != NULL; j++) {
const Option *option = &options[j];
/* A given option has several aliases. */
for (k = 0; ; k++) {
const Argument *argument;
size_t length;
argument = &option->arguments[k];
/* End of aliases for this option. */
if (!argument->name)
break;
length = strlen(argument->name);
if (strncmp(arg, argument->name, length) != 0)
continue;
/* Avoid ambiguities. */
if (strlen(arg) > length
&& arg[length] != argument->separator) {
print_error_separator(tracee, argument);
return -1;
}
/* No option value. */
if (!argument->value) {
status = option->handler(tracee, cli, NULL);
if (status < 0)
return -1;
goto known_option;
}
/* Value coalesced with to its option. */
if (argument->separator == arg[length]) {
assert(strlen(arg) >= length);
status = option->handler(tracee, cli, &arg[length + 1]);
if (status < 0)
return -1;
goto known_option;
}
/* Avoid ambiguities. */
if (argument->separator != ' ') {
print_error_separator(tracee, argument);
return -1;
}
/* Short option with a separated value. */
handler = option->handler;
goto known_option;
}
}
note(tracee, ERROR, USER, "unknown option '%s'.", arg);
return -1;
known_option:
if (handler != NULL && i == argc - 1) {
note(tracee, ERROR, USER, "missing value for option '%s'.", arg);
return -1;
}
}
argc_offset = i;
status = pre_initialize_bindings(tracee, cli);
if (status < 0)
return -1;
/* The guest rootfs is now known: bindings specified by the
* user (tracee->bindings.user) can be canonicalized. */
status = initialize_bindings(tracee);
if (status < 0)
return -1;
/* Bindings are now installed (tracee->bindings.guest &
* tracee->bindings.host): the current working directory can
* be canonicalized. */
status = initialize_cwd(tracee);
if (status < 0)
return -1;
/* Bindings are now installed and the current working
* directory is canonicalized: resolve path to @tracee->exe
* and configure @tracee->cmdline. */
status = initialize_exe(tracee, argv[argc_offset]);
if (status < 0)
return -1;
print_config(tracee, &argv[argc_offset]);
return argc_offset;
}
bool exit_failure = true;
int main(int argc, char *const argv[])
{
Tracee *tracee;
int status;
/* Configure the memory allocator. */
talloc_enable_leak_report();
#if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2
talloc_set_log_stderr();
#endif
/* Pre-create the first tracee (pid == 0). */
tracee = get_tracee(NULL, 0, true);
if (tracee == NULL)
goto error;
tracee->pid = getpid();
/* Set verboseness from env variable, may be overriden by option */
{
const char *verbose_env = getenv("PROOT_VERBOSE");
if (verbose_env != NULL) {
tracee->verbose = strtol(verbose_env, NULL, 10);
global_verbose_level = tracee->verbose;
}
}
/* Pre-configure the first tracee. */
status = parse_config(tracee, argc, argv);
if (status < 0)
goto error;
/* Start the first tracee. */
status = launch_process(tracee, &argv[status]);
if (status < 0) {
print_execve_help(tracee, tracee->exe, status);
goto error;
}
/* Start tracing the first tracee and all its children. */
exit(event_loop());
error:
TALLOC_FREE(tracee);
if (exit_failure) {
exit(EXIT_FAILURE);
}
else
exit(EXIT_SUCCESS);
}
/**
* Convert @value into an integer, then put the result into
* *@variable. This function prints a warning and returns -1 if a
* conversion error occured, otherwise it returns 0.
*/
int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option)
{
char *end_ptr = NULL;
errno = 0;
*variable = strtol(value, &end_ptr, 10);
if (errno != 0 || end_ptr == value) {
note(tracee, ERROR, USER, "option `%s` expects an integer value.", option);
return -1;
}
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */
#ifndef CLI_H
#define CLI_H
#include <stddef.h>
#include "tracee/tracee.h"
#include "attribute.h"
typedef struct {
const char *name;
char separator;
const char *value;
} Argument;
struct Cli;
typedef int (*option_handler_t)(Tracee *tracee, const struct Cli *cli, const char *value);
typedef struct {
const char *class;
option_handler_t handler;
const char *description;
const char *detail;
Argument arguments[5];
} Option;
#define END_OF_OPTIONS { .class = NULL, \
.arguments = {{ .name = NULL, .separator = '\0', .value = NULL }}, \
.handler = NULL, \
.description = NULL, \
.detail = NULL \
}
typedef struct Cli {
const char *name;
const char *version;
const char *subtitle;
const char *synopsis;
const char *colophon;
const char *logo;
void *private;
const Option options[];
} Cli;
extern int pre_initialize_bindings(Tracee *, const Cli *);
extern const Cli *get_proot_cli(TALLOC_CTX *context);
extern void print_usage(Tracee *tracee, const Cli *cli, bool detailed);
extern void print_version(const Cli *cli);
extern int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option);
extern bool exit_failure;
#endif /* CLI_H */
+95
View File
@@ -0,0 +1,95 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <errno.h> /* errno, */
#include <string.h> /* strerror(3), */
#include <stdarg.h> /* va_*, */
#include <stdio.h> /* vfprintf(3), */
#include <limits.h> /* INT_MAX, */
#include "cli/note.h"
#include "tracee/tracee.h"
int global_verbose_level;
const char *global_tool_name;
/**
* Print @message to the standard error stream according to its
* @severity and @origin.
*/
void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...)
{
const char *tool_name;
va_list extra_params;
int verbose_level;
tool_name = global_tool_name ?: "";
if (tracee == NULL) {
verbose_level = global_verbose_level;
}
else
verbose_level = tracee->verbose;
if (verbose_level < 0 && severity != ERROR)
return;
switch (severity) {
case WARNING:
fprintf(stderr, "%s warning: ", tool_name);
break;
case ERROR:
fprintf(stderr, "%s error: ", tool_name);
break;
case INFO:
default:
fprintf(stderr, "%s info: ", tool_name);
break;
}
if (origin == TALLOC)
fprintf(stderr, "talloc: ");
va_start(extra_params, message);
vfprintf(stderr, message, extra_params);
va_end(extra_params);
switch (origin) {
case SYSTEM:
fprintf(stderr, ": ");
perror(NULL);
break;
case TALLOC:
break;
case INTERNAL:
case USER:
default:
fprintf(stderr, "\n");
break;
}
return;
}
+54
View File
@@ -0,0 +1,54 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef NOTE_H
#define NOTE_H
#include "tracee/tracee.h"
#include "attribute.h"
/* Specify where a notice is coming from. */
typedef enum {
SYSTEM,
INTERNAL,
USER,
TALLOC,
} Origin;
/* Specify the severity of a notice. */
typedef enum {
ERROR,
WARNING,
INFO,
} Severity;
#define VERBOSE(tracee, level, message, args...) do { \
if (tracee == NULL || tracee->verbose >= (level)) \
note(tracee, INFO, INTERNAL, (message), ## args); \
} while (0)
extern void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) FORMAT(printf, 4, 5);
extern int global_verbose_level;
extern const char *global_tool_name;
#endif /* NOTE_H */
+152
View File
@@ -0,0 +1,152 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <string.h> /* str*(3), */
#include <assert.h> /* assert(3), */
#include <stdio.h> /* printf(3), fflush(3), */
#include <unistd.h> /* write(2), */
#include "cli/cli.h"
#include "cli/note.h"
#include "path/binding.h"
#include "attribute.h"
/* These should be included last. */
#include "cli/proot.h"
static int handle_option_r(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
Binding *binding;
/* ``chroot $PATH`` is semantically equivalent to ``mount
* --bind $PATH /``. */
binding = new_binding(tracee, value, "/", true);
if (binding == NULL)
return -1;
return 0;
}
static int handle_option_b(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
char *host;
char *guest;
host = talloc_strdup(tracee->ctx, value);
if (host == NULL) {
note(tracee, ERROR, INTERNAL, "can't allocate memory");
return -1;
}
guest = strchr(host, ':');
if (guest != NULL) {
*guest = '\0';
guest++;
}
new_binding(tracee, host, guest, true);
return 0;
}
static int handle_option_w(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
tracee->fs->cwd = talloc_strdup(tracee->fs, value);
if (tracee->fs->cwd == NULL)
return -1;
talloc_set_name_const(tracee->fs->cwd, "$cwd");
return 0;
}
static int handle_option_kill_on_exit(Tracee *tracee, const Cli *cli UNUSED, const char *value UNUSED)
{
tracee->killall_on_exit = true;
return 0;
}
static int handle_option_v(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
int status;
status = parse_integer_option(tracee, &tracee->verbose, value, "-v");
if (status < 0)
return status;
global_verbose_level = tracee->verbose;
return 0;
}
extern unsigned char WEAK _binary_licenses_start;
extern unsigned char WEAK _binary_licenses_end;
static int handle_option_V(Tracee *tracee UNUSED, const Cli *cli, const char *value UNUSED)
{
size_t size;
print_version(cli);
printf("\n%s\n", cli->colophon);
fflush(stdout);
size = &_binary_licenses_end - &_binary_licenses_start;
if (size > 0)
write(1, &_binary_licenses_start, size);
exit_failure = false;
return -1;
}
static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value UNUSED)
{
print_usage(tracee, cli, true);
exit_failure = false;
return -1;
}
/**
* Initialize @tracee's fields that are mandatory for PRoot but that
* are not required on the command line, i.e. "-w" and "-r".
*/
int pre_initialize_bindings(Tracee *tracee, const Cli *cli)
{
int status;
/* Default to "." if no CWD were specified. */
if (tracee->fs->cwd == NULL) {
status = handle_option_w(tracee, cli, ".");
if (status < 0)
return -1;
}
/* The default guest rootfs is "/" if none was specified. */
if (get_root(tracee) == NULL) {
status = handle_option_r(tracee, cli, "/");
if (status < 0)
return -1;
}
return 0;
}
const Cli *get_proot_cli(TALLOC_CTX *context UNUSED)
{
global_tool_name = proot_cli.name;
return &proot_cli;
}
+127
View File
@@ -0,0 +1,127 @@
/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */
#ifndef PROOT_CLI_H
#define PROOT_CLI_H
#include "cli/cli.h"
#ifndef VERSION
#define VERSION "5.1.0"
#endif
static int handle_option_r(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_b(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_w(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_v(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_V(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_kill_on_exit(Tracee *tracee, const Cli *cli, const char *value);
static Cli proot_cli = {
.version = VERSION,
.name = "proot",
.subtitle = "chroot, mount --bind, and binfmt_misc without privilege/setup",
.synopsis = "proot [option] ... [command]",
.colophon = "Visit http://proot.me for help, bug reports, suggestions, patchs, ...\n\
Copyright (C) 2015 STMicroelectronics, licensed under GPL v2 or later.",
.logo = "\
_____ _____ ___\n\
| __ \\ __ \\_____ _____| |_\n\
| __/ / _ \\/ _ \\ _|\n\
|__| |__|__\\_____/\\_____/\\____|",
.options = {
{ .class = "Regular options",
.arguments = {
{ .name = "-r", .separator = ' ', .value = "path" },
{ .name = "--rootfs", .separator = '=', .value = "path" },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_r,
.description = "Use *path* as the new guest root file-system, default is /.",
.detail = "\tThe specified path typically contains a Linux distribution where\n\
\tall new programs will be confined. The default rootfs is /\n\
\twhen none is specified, this makes sense when the bind mechanism\n\
\tis used to relocate host files and directories, see the -b\n\
\toption and the Examples section for details.\n\
\t\n\
\tIt is recommended to use the -R or -S options instead.",
},
{ .class = "Regular options",
.arguments = {
{ .name = "-b", .separator = ' ', .value = "path" },
{ .name = "--bind", .separator = '=', .value = "path" },
{ .name = "-m", .separator = ' ', .value = "path" },
{ .name = "--mount", .separator = '=', .value = "path" },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_b,
.description = "Make the content of *path* accessible in the guest rootfs.",
.detail = "\tThis option makes any file or directory of the host rootfs\n\
\taccessible in the confined environment just as if it were part of\n\
\tthe guest rootfs. By default the host path is bound to the same\n\
\tpath in the guest rootfs but users can specify any other location\n\
\twith the syntax: -b *host_path*:*guest_location*. If the\n\
\tguest location is a symbolic link, it is dereferenced to ensure\n\
\tthe new content is accessible through all the symbolic links that\n\
\tpoint to the overlaid content. In most cases this default\n\
\tbehavior shouldn't be a problem, although it is possible to\n\
\texplicitly not dereference the guest location by appending it the\n\
\t! character: -b *host_path*:*guest_location!*.",
},
{ .class = "Regular options",
.arguments = {
{ .name = "-w", .separator = ' ', .value = "path" },
{ .name = "--pwd", .separator = '=', .value = "path" },
{ .name = "--cwd", .separator = '=', .value = "path" },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_w,
.description = "Set the initial working directory to *path*.",
.detail = "\tSome programs expect to be launched from a given directory but do\n\
\tnot perform any chdir by themselves. This option avoids the\n\
\tneed for running a shell and then entering the directory manually.",
},
{ .class = "Regular options",
.arguments = {
{ .name = "--kill-on-exit", .separator = '\0', .value = NULL },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_kill_on_exit,
.description = "Kill all processes on command exit.",
.detail = "\tWhen the executed command leaves orphan or detached processes\n\
\taround, proot waits until all processes possibly terminate. This option forces\n\
\tthe immediate termination of all tracee processes when the main command exits.",
},
{ .class = "Regular options",
.arguments = {
{ .name = "-v", .separator = ' ', .value = "value" },
{ .name = "--verbose", .separator = '=', .value = "value" },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_v,
.description = "Set the level of debug information to *value*.",
.detail = "\tThe higher the integer value is, the more detailed debug\n\
\tinformation is printed to the standard error stream. A negative\n\
\tvalue makes PRoot quiet except on fatal errors.",
},
{ .class = "Regular options",
.arguments = {
{ .name = "-V", .separator = '\0', .value = NULL },
{ .name = "--version", .separator = '\0', .value = NULL },
{ .name = "--about", .separator = '\0', .value = NULL },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_V,
.description = "Print version, copyright, license and contact, then exit.",
.detail = "",
},
{ .class = "Regular options",
.arguments = {
{ .name = "-h", .separator = '\0', .value = NULL },
{ .name = "--help", .separator = '\0', .value = NULL },
{ .name = "--usage", .separator = '\0', .value = NULL },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_h,
.description = "Print the version and the command-line usage, then exit.",
.detail = "",
},
END_OF_OPTIONS,
},
};
#endif /* PROOT_CLI_H */
+265
View File
@@ -0,0 +1,265 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef COMPAT_H
#define COMPAT_H
/* Local definitions for compatibility with old and/or broken distros... */
# ifndef AT_NULL
# define AT_NULL 0
# endif
# ifndef AT_PHDR
# define AT_PHDR 3
# endif
# ifndef AT_PHENT
# define AT_PHENT 4
# endif
# ifndef AT_PHNUM
# define AT_PHNUM 5
# endif
# ifndef AT_BASE
# define AT_BASE 7
# endif
# ifndef AT_ENTRY
# define AT_ENTRY 9
# endif
# ifndef AT_RANDOM
# define AT_RANDOM 25
# endif
# ifndef AT_EXECFN
# define AT_EXECFN 31
# endif
# ifndef AT_SYSINFO
# define AT_SYSINFO 32
# endif
# ifndef AT_SYSINFO_EHDR
# define AT_SYSINFO_EHDR 33
# endif
# ifndef AT_FDCWD
# define AT_FDCWD -100
# endif
# ifndef AT_SYMLINK_FOLLOW
# define AT_SYMLINK_FOLLOW 0x400
# endif
# ifndef AT_REMOVEDIR
# define AT_REMOVEDIR 0x200
# endif
# ifndef AT_SYMLINK_NOFOLLOW
# define AT_SYMLINK_NOFOLLOW 0x100
# endif
# ifndef IN_DONT_FOLLOW
# define IN_DONT_FOLLOW 0x02000000
# endif
# ifndef WIFCONTINUED
# define WIFCONTINUED(status) ((status) == 0xffff)
# endif
# ifndef PTRACE_GETREGS
# define PTRACE_GETREGS 12
# endif
# ifndef PTRACE_SETREGS
# define PTRACE_SETREGS 13
# endif
# ifndef PTRACE_GETFPREGS
# define PTRACE_GETFPREGS 14
# endif
# ifndef PTRACE_SETFPREGS
# define PTRACE_SETFPREGS 15
# endif
# ifndef PTRACE_GETFPXREGS
# define PTRACE_GETFPXREGS 18
# endif
# ifndef PTRACE_SETFPXREGS
# define PTRACE_SETFPXREGS 19
# endif
# ifndef PTRACE_SETOPTIONS
# define PTRACE_SETOPTIONS 0x4200
# endif
# ifndef PTRACE_GETEVENTMSG
# define PTRACE_GETEVENTMSG 0x4201
# endif
# ifndef PTRACE_GETREGSET
# define PTRACE_GETREGSET 0x4204
# endif
# ifndef PTRACE_SETREGSET
# define PTRACE_SETREGSET 0x4205
# endif
# ifndef PTRACE_SEIZE
# define PTRACE_SEIZE 0x4206
# endif
# ifndef PTRACE_INTERRUPT
# define PTRACE_INTERRUPT 0x4207
# endif
# ifndef PTRACE_LISTEN
# define PTRACE_LISTEN 0x4208
# endif
# ifndef PTRACE_O_TRACESYSGOOD
# define PTRACE_O_TRACESYSGOOD 0x00000001
# endif
# ifndef PTRACE_O_TRACEFORK
# define PTRACE_O_TRACEFORK 0x00000002
# endif
# ifndef PTRACE_O_TRACEVFORK
# define PTRACE_O_TRACEVFORK 0x00000004
# endif
# ifndef PTRACE_O_TRACECLONE
# define PTRACE_O_TRACECLONE 0x00000008
# endif
# ifndef PTRACE_O_TRACEEXEC
# define PTRACE_O_TRACEEXEC 0x00000010
# endif
# ifndef PTRACE_O_TRACEVFORKDONE
# define PTRACE_O_TRACEVFORKDONE 0x00000020
# endif
# ifndef PTRACE_O_TRACEEXIT
# define PTRACE_O_TRACEEXIT 0x00000040
# endif
# ifndef PTRACE_O_TRACESECCOMP
# define PTRACE_O_TRACESECCOMP 0x00000080
# endif
# ifndef PTRACE_EVENT_FORK
# define PTRACE_EVENT_FORK 1
# endif
# ifndef PTRACE_EVENT_VFORK
# define PTRACE_EVENT_VFORK 2
# endif
# ifndef PTRACE_EVENT_CLONE
# define PTRACE_EVENT_CLONE 3
# endif
# ifndef PTRACE_EVENT_EXEC
# define PTRACE_EVENT_EXEC 4
# endif
# ifndef PTRACE_EVENT_VFORK_DONE
# define PTRACE_EVENT_VFORK_DONE 5
# endif
# ifndef PTRACE_EVENT_EXIT
# define PTRACE_EVENT_EXIT 6
# endif
# ifndef PTRACE_EVENT_SECCOMP
# define PTRACE_EVENT_SECCOMP 7
# endif
# ifndef PTRACE_EVENT_SECCOMP2
# if PTRACE_EVENT_SECCOMP == 7
# define PTRACE_EVENT_SECCOMP2 8
# elif PTRACE_EVENT_SECCOMP == 8
# define PTRACE_EVENT_SECCOMP2 7
# else
# error "unknown PTRACE_EVENT_SECCOMP value"
# endif
# endif
# ifndef PTRACE_SET_SYSCALL
# define PTRACE_SET_SYSCALL 23
# endif
# ifndef PTRACE_GET_THREAD_AREA
# define PTRACE_GET_THREAD_AREA 25
# endif
# ifndef PTRACE_SET_THREAD_AREA
# define PTRACE_SET_THREAD_AREA 26
# endif
# ifndef PTRACE_GETVFPREGS
# define PTRACE_GETVFPREGS 27
# endif
# ifndef PTRACE_ARCH_PRCTL
# define PTRACE_ARCH_PRCTL 30
# endif
# ifndef ARCH_SET_GS
# define ARCH_SET_GS 0x1001
# endif
# ifndef ARCH_SET_FS
# define ARCH_SET_FS 0x1002
# endif
# ifndef ARCH_GET_GS
# define ARCH_GET_FS 0x1003
# endif
# ifndef ARCH_GET_FS
# define ARCH_GET_GS 0x1004
# endif
# ifndef PTRACE_SINGLEBLOCK
# define PTRACE_SINGLEBLOCK 33
# endif
# ifndef ADDR_NO_RANDOMIZE
# define ADDR_NO_RANDOMIZE 0x0040000
# endif
# ifndef SYS_ACCEPT4
# define SYS_ACCEPT4 18
# endif
# ifndef TALLOC_FREE
# define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx = NULL; } while(0)
# endif
# ifndef PR_SET_NAME
# define PR_SET_NAME 15
# endif
# ifndef PR_SET_NO_NEW_PRIVS
# define PR_SET_NO_NEW_PRIVS 38
# endif
# ifndef PR_SET_SECCOMP
# define PR_SET_SECCOMP 22
# endif
# ifndef SECCOMP_MODE_FILTER
# define SECCOMP_MODE_FILTER 2
# endif
# ifndef talloc_get_type_abort
# define talloc_get_type_abort talloc_get_type
# endif
# ifndef FUTEX_PRIVATE_FLAG
# define FUTEX_PRIVATE_FLAG 128
# endif
# ifndef EFD_SEMAPHORE
# define EFD_SEMAPHORE 1
# endif
# ifndef F_DUPFD_CLOEXEC
# define F_DUPFD_CLOEXEC 1030
# endif
# ifndef O_RDONLY
# define O_RDONLY 00000000
# endif
# ifndef O_CLOEXEC
# define O_CLOEXEC 02000000
# endif
# ifndef MAP_PRIVATE
# define MAP_PRIVATE 0x02
# endif
# ifndef MAP_FIXED
# define MAP_FIXED 0x10
# endif
# ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS 0x20
# endif
# ifndef PROT_READ
# define PROT_READ 0x1
# endif
# ifndef PROT_WRITE
# define PROT_WRITE 0x2
# endif
# ifndef PROT_EXEC
# define PROT_EXEC 0x4
# endif
# ifndef PROT_GROWSDOWN
# define PROT_GROWSDOWN 0x01000000
# endif
# ifndef NT_ARM_SYSTEM_CALL
# define NT_ARM_SYSTEM_CALL 0x404
# endif
# ifndef SYS_SECCOMP
# define SYS_SECCOMP 1
# endif
#endif /* COMPAT_H */
+336
View File
@@ -0,0 +1,336 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <linux/limits.h> /* ARG_MAX, */
#include <assert.h> /* assert(3), */
#include <string.h> /* strlen(3), memcmp(3), memcpy(3), */
#include <strings.h> /* bzero(3), */
#include <stdbool.h> /* bool, true, false, */
#include <errno.h> /* E*, */
#include <stdarg.h> /* va_*, */
#include <stdint.h> /* uint32_t, */
#include <talloc.h> /* talloc_*, */
#include "arch.h"
#include "tracee/tracee.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
struct mixed_pointer {
/* Pointer -- in tracee's address space -- to the current
* object, if local == NULL. */
word_t remote;
/* Pointer -- in tracer's address space -- to the current
* object, if local != NULL. */
void *local;
};
#include "execve/aoxp.h"
/**
* Read string pointed to by @array[@index] from tracee's memory, then
* make @local_pointer points to the locally *cached* version. This
* function returns -errno when an error occured, otherwise 0.
*/
int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **local_pointer)
{
char tmp[ARG_MAX];
int status;
assert(index < array->length);
/* Already cached locally? */
if (array->_xpointers[index].local != NULL)
goto end;
/* Remote NULL is mapped to local NULL. */
if (array->_xpointers[index].remote == 0) {
array->_xpointers[index].local = NULL;
goto end;
}
/* Copy locally the remote string into a temporary buffer. */
status = read_string(TRACEE(array), tmp, array->_xpointers[index].remote, ARG_MAX);
if (status < 0)
return status;
if (status >= ARG_MAX)
return -ENOMEM;
/* Save the local string in a "persistent" buffer. */
array->_xpointers[index].local = talloc_strdup(array, tmp);
if (array->_xpointers[index].local == NULL)
return -ENOMEM;
end:
*local_pointer = array->_xpointers[index].local;
return 0;
}
/**
* This function returns the number of bytes of the string pointed to
* by @array[@index], otherwise -errno if an error occured.
*/
int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index)
{
char *string;
int status;
assert(index < array->length);
status = read_xpointee_as_string(array, index, &string);
if (status < 0)
return status;
if (string == NULL)
return 0;
return strlen(string) + 1;
}
/**
* Make @array[@index] points to a copy of the string pointed to by
* @string. This function returns -errno when an error occured,
* otherwise 0.
*/
int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string)
{
assert(index < array->length);
array->_xpointers[index].local = talloc_strdup(array, string);
if (array->_xpointers[index].local == NULL)
return -ENOMEM;
return 0;
}
/**
* Make @array[@index ... @index + @nb_xpointees] points to a copy of
* the variadic arguments. This function returns -errno when an error
* occured, otherwise 0.
*/
int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...)
{
va_list va_xpointees;
int status;
size_t i;
va_start(va_xpointees, nb_xpointees);
for (i = 0; i < nb_xpointees; i++) {
void *object = va_arg(va_xpointees, void *);
status = write_xpointee(array, index + i, object);
if (status < 0)
goto end;
}
status = 0;
end:
va_end(va_xpointees);
return status;
}
/**
* Resize the @array at the given @index by the @delta_nb_entries.
* This function returns -errno when an error occured, otherwise 0.
*/
int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t delta_nb_entries)
{
size_t nb_moved_entries;
size_t new_length;
void *tmp;
assert(index < array->length);
if (delta_nb_entries == 0)
return 0;
new_length = array->length + delta_nb_entries;
nb_moved_entries = array->length - index;
if (delta_nb_entries > 0) {
tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index,
nb_moved_entries * sizeof(XPointer));
bzero(array->_xpointers + index, delta_nb_entries * sizeof(XPointer));
}
else {
assert(delta_nb_entries <= 0);
assert(index >= (size_t) -delta_nb_entries);
memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index,
nb_moved_entries * sizeof(XPointer));
tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
}
array->length = new_length;
return 0;
}
/**
* Copy into *@array_ the pointer array pointed to by @reg from
* @tracee's memory space. Only the first @nb_entries are copied,
* unless it is 0 then all the entries up to the NULL pointer are
* copied. This function returns -errno when an error occured,
* otherwise 0.
*/
int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array_, Reg reg, size_t nb_entries)
{
word_t pointer = 1; /* ie. != 0 */
word_t address;
ArrayOfXPointers *array;
size_t i;
assert(array_ != NULL);
*array_ = talloc_zero(tracee->ctx, ArrayOfXPointers);
if (*array_ == NULL)
return -ENOMEM;
array = *array_;
address = peek_reg(tracee, CURRENT, reg);
for (i = 0; nb_entries != 0 ? i < nb_entries : pointer != 0; i++) {
void *tmp = talloc_realloc(array, array->_xpointers, XPointer, i + 1);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
pointer = peek_word(tracee, address + i * sizeof_word(tracee));
if (errno != 0)
return -errno;
array->_xpointers[i].remote = pointer;
array->_xpointers[i].local = NULL;
}
array->length = i;
/* By default, assume it is an array of string pointers. */
array->sizeof_xpointee = sizeof_xpointee_as_string;
array->write_xpointee = (write_xpointee_t) write_xpointee_as_string;
return 0;
}
/**
* Copy @array into tracee's memory space, then put in @reg the
* address where it was copied. This function returns -errno if an
* error occured, otherwise 0.
*/
int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg)
{
Tracee *tracee;
struct iovec *local;
size_t local_count;
size_t total_size;
word_t *pod_array;
word_t tracee_ptr;
int status;
size_t i;
/* Nothing to do, for sure. */
if (array == NULL)
return 0;
tracee = TRACEE(array);
/* The pointer table is a POD array in the tracee's memory. */
pod_array = talloc_zero_size(tracee->ctx, array->length * sizeof_word(tracee));
if (pod_array == NULL)
return -ENOMEM;
/* There's one vector per modified pointee + one vector for the
* pod array. */
local = talloc_zero_array(tracee->ctx, struct iovec, array->length + 1);
if (local == NULL)
return -ENOMEM;
/* The pod array is expected to be at the beginning of the
* allocated memory by the caller. */
total_size = array->length * sizeof_word(tracee);
local[0].iov_base = pod_array;
local[0].iov_len = total_size;
local_count = 1;
/* Create one vector for each modified pointee. */
for (i = 0; i < array->length; i++) {
ssize_t size;
if (array->_xpointers[i].local == NULL)
continue;
/* At this moment, we only know the offsets in the
* tracee's memory block. */
array->_xpointers[i].remote = total_size;
size = sizeof_xpointee(array, i);
if (size < 0)
return size;
total_size += size;
local[local_count].iov_base = array->_xpointers[i].local;
local[local_count].iov_len = size;
local_count++;
}
/* Nothing has changed, don't update anything. */
if (local_count == 1)
return 0;
assert(local_count < array->length + 1);
/* Modified pointees and the pod array are stored in a tracee's
* memory block. */
tracee_ptr = alloc_mem(tracee, total_size);
if (tracee_ptr == 0)
return -E2BIG;
/* Now, we know the absolute addresses in the tracee's
* memory. */
for (i = 0; i < array->length; i++) {
if (array->_xpointers[i].local != NULL)
array->_xpointers[i].remote += tracee_ptr;
if (is_32on64_mode(tracee))
((uint32_t *) pod_array)[i] = array->_xpointers[i].remote;
else
pod_array[i] = array->_xpointers[i].remote;
}
/* Write all the modified pointees and the pod array at once. */
status = writev_data(tracee, tracee_ptr, local, local_count);
if (status < 0)
return status;
poke_reg(tracee, reg, tracee_ptr);
return 0;
}
+63
View File
@@ -0,0 +1,63 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef AOXP_H
#define AOXP_H
#include <stdbool.h>
#include "tracee/reg.h"
#include "arch.h"
typedef struct array_of_xpointers ArrayOfXPointers;
typedef int (*write_xpointee_t)(ArrayOfXPointers *array, size_t index, const void *object);
typedef int (*sizeof_xpointee_t)(ArrayOfXPointers *array, size_t index);
typedef struct mixed_pointer XPointer;
struct array_of_xpointers {
XPointer *_xpointers;
size_t length;
write_xpointee_t write_xpointee;
sizeof_xpointee_t sizeof_xpointee;
};
static inline int write_xpointee(ArrayOfXPointers *array, size_t index, const void *object)
{
return array->write_xpointee(array, index, object);
}
static inline int sizeof_xpointee(ArrayOfXPointers *array, size_t index)
{
return array->sizeof_xpointee(array, index);
}
extern int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t nb_delta_entries);
extern int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array, Reg reg, size_t nb_entries);
extern int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg);
extern int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **string);
extern int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string);
extern int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...);
extern int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index);
#endif /* AOXP_H */
+150
View File
@@ -0,0 +1,150 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <linux/auxvec.h> /* AT_*, */
#include <assert.h> /* assert(3), */
#include <errno.h> /* E*, */
#include <unistd.h> /* write(3), close(3), */
#include <sys/types.h> /* open(2), */
#include <sys/stat.h> /* open(2), */
#include <fcntl.h> /* open(2), */
#include "execve/auxv.h"
#include "syscall/sysnum.h"
#include "tracee/tracee.h"
#include "tracee/mem.h"
#include "tracee/reg.h"
#include "tracee/abi.h"
#include "arch.h"
/**
* Add the given vector [@type, @value] to @vectors. This function
* returns -errno if an error occurred, otherwise 0.
*/
int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value)
{
ElfAuxVector *tmp;
size_t nb_vectors;
assert(*vectors != NULL);
nb_vectors = talloc_array_length(*vectors);
/* Sanity checks. */
assert(nb_vectors > 0);
assert((*vectors)[nb_vectors - 1].type == AT_NULL);
tmp = talloc_realloc(talloc_parent(*vectors), *vectors, ElfAuxVector, nb_vectors + 1);
if (tmp == NULL)
return -ENOMEM;
*vectors = tmp;
/* Replace the sentinel with the new vector. */
(*vectors)[nb_vectors - 1].type = type;
(*vectors)[nb_vectors - 1].value = value;
/* Restore the sentinel. */
(*vectors)[nb_vectors].type = AT_NULL;
(*vectors)[nb_vectors].value = 0;
return 0;
}
/**
* Get the address of the the ELF auxiliary vectors table for the
* given @tracee. This function returns 0 if an error occurred.
*/
word_t get_elf_aux_vectors_address(const Tracee *tracee)
{
word_t address;
word_t data;
/* Sanity check: this works only in execve sysexit. */
assert(IS_IN_SYSEXIT2(tracee, PR_execve));
/* Right after execve, the stack layout is:
*
* argc, argv[0], ..., 0, envp[0], ..., 0, auxv[0].type, auxv[0].value, ..., 0, 0
*/
address = peek_reg(tracee, CURRENT, STACK_POINTER);
/* Read: argc */
data = peek_word(tracee, address);
if (errno != 0)
return 0;
/* Skip: argc, argv, 0 */
address += (1 + data + 1) * sizeof_word(tracee);
/* Skip: envp, 0 */
do {
data = peek_word(tracee, address);
if (errno != 0)
return 0;
address += sizeof_word(tracee);
} while (data != 0);
return address;
}
/**
* Fetch ELF auxiliary vectors stored at the given @address in
* @tracee's memory. This function returns NULL if an error occurred,
* otherwise it returns a pointer to the new vectors, in an ABI
* independent form (the Talloc parent of this pointer is
* @tracee->ctx).
*/
ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address)
{
ElfAuxVector *vectors = NULL;
ElfAuxVector vector;
int status;
/* It is assumed the sentinel always exists. */
vectors = talloc_array(tracee->ctx, ElfAuxVector, 1);
if (vectors == NULL)
return NULL;
vectors[0].type = AT_NULL;
vectors[0].value = 0;
while (1) {
vector.type = peek_word(tracee, address);
if (errno != 0)
return NULL;
address += sizeof_word(tracee);
if (vector.type == AT_NULL)
break; /* Already added. */
vector.value = peek_word(tracee, address);
if (errno != 0)
return NULL;
address += sizeof_word(tracee);
status = add_elf_aux_vector(&vectors, vector.type, vector.value);
if (status < 0)
return NULL;
}
return vectors;
}
+38
View File
@@ -0,0 +1,38 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef AUXV
#define AUXV
#include "tracee/tracee.h"
#include "arch.h"
typedef struct elf_aux_vector {
word_t type;
word_t value;
} ElfAuxVector;
extern word_t get_elf_aux_vectors_address(const Tracee *tracee);
extern ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address);
extern int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value);
#endif /* AUXV */
+144
View File
@@ -0,0 +1,144 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <fcntl.h> /* open(2), */
#include <unistd.h> /* read(2), close(2), */
#include <errno.h> /* EACCES, ENOTSUP, */
#include <stdint.h> /* UINT64_MAX, */
#include <limits.h> /* PATH_MAX, */
#include <string.h> /* str*(3), memcpy(3), */
#include <assert.h> /* assert(3), */
#include <talloc.h> /* talloc_*, */
#include <stdbool.h> /* bool, true, false, */
#include "execve/elf.h"
#include "tracee/tracee.h"
#include "cli/note.h"
#include "arch.h"
#include "compat.h"
/**
* Open the ELF file @t_path and extract its header into @elf_header.
* This function returns -errno if an error occured, otherwise the
* file descriptor for @t_path.
*/
int open_elf(const char *t_path, ElfHeader *elf_header)
{
int fd;
int status;
/*
* Read the ELF header.
*/
fd = open(t_path, O_RDONLY);
if (fd < 0)
return -errno;
/* Check if it is an ELF file. */
status = read(fd, elf_header, sizeof(ElfHeader));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(ElfHeader)
|| ELF_IDENT(*elf_header, 0) != 0x7f
|| ELF_IDENT(*elf_header, 1) != 'E'
|| ELF_IDENT(*elf_header, 2) != 'L'
|| ELF_IDENT(*elf_header, 3) != 'F') {
status = -ENOEXEC;
goto end;
}
/* Check if it is a known class (32-bit or 64-bit). */
if ( !IS_CLASS32(*elf_header)
&& !IS_CLASS64(*elf_header)) {
status = -ENOEXEC;
goto end;
}
status = 0;
end:
/* Delayed error handling. */
if (status < 0) {
close(fd);
return status;
}
return fd;
}
/**
* Invoke @callback(..., @data) for each program headers from the
* specified ELF file (referenced by @fd, with the given @elf_header).
* This function returns -errno if an error occured, or it returns
* immediately the value != 0 returned by @callback, otherwise 0.
*/
int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header,
program_headers_iterator_t callback, void *data)
{
ProgramHeader program_header;
uint64_t elf_phoff;
uint16_t elf_phentsize;
uint16_t elf_phnum;
int status;
int i;
/* Get class-specific fields. */
elf_phnum = ELF_FIELD(*elf_header, phnum);
elf_phentsize = ELF_FIELD(*elf_header, phentsize);
elf_phoff = ELF_FIELD(*elf_header, phoff);
/*
* Some sanity checks regarding the current
* support of the ELF specification in PRoot.
*/
if (elf_phnum >= 0xffff) {
note(tracee, WARNING, INTERNAL, "%d: big PH tables are not yet supported.", fd);
return -ENOTSUP;
}
if (!KNOWN_PHENTSIZE(*elf_header, elf_phentsize)) {
note(tracee, WARNING, INTERNAL, "%d: unsupported size of program header.", fd);
return -ENOTSUP;
}
status = (int) lseek(fd, elf_phoff, SEEK_SET);
if (status < 0)
return -errno;
for (i = 0; i < elf_phnum; i++) {
status = read(fd, &program_header, elf_phentsize);
if (status != elf_phentsize)
return (status < 0 ? -errno : -ENOTSUP);
status = callback(elf_header, &program_header, data);
if (status != 0)
return status;
}
return 0;
}
+162
View File
@@ -0,0 +1,162 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef ELF_H
#define ELF_H
#define EI_NIDENT 16
#include <stdint.h>
#include <stdbool.h>
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} ElfHeader32;
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} ElfHeader64;
typedef union {
ElfHeader32 class32;
ElfHeader64 class64;
} ElfHeader;
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} ProgramHeader32;
typedef struct {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} ProgramHeader64;
typedef union {
ProgramHeader32 class32;
ProgramHeader64 class64;
} ProgramHeader;
/* Object type: */
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
/* Segment flags: */
#define PF_X 1
#define PF_W 2
#define PF_R 4
typedef enum {
PT_LOAD = 1,
PT_DYNAMIC = 2,
PT_INTERP = 3,
PT_GNU_STACK = 0x6474e551,
} SegmentType;
typedef enum {
DT_STRTAB = 5,
DT_RPATH = 15,
DT_RUNPATH = 29
} DynamicType;
/* The following macros are also compatible with ELF 64-bit. */
#define ELF_IDENT(header, index) (header).class32.e_ident[(index)]
#define ELF_CLASS(header) ELF_IDENT(header, 4)
#define IS_CLASS32(header) (ELF_CLASS(header) == 1)
#define IS_CLASS64(header) (ELF_CLASS(header) == 2)
/* Helper to access a @field of the structure ElfHeaderXX. */
#define ELF_FIELD(header, field) \
(IS_CLASS64(header) \
? (header).class64. e_ ## field \
: (header).class32. e_ ## field)
/* Helper to access a @field of the structure ProgramHeaderXX */
#define PROGRAM_FIELD(ehdr, phdr, field) \
(IS_CLASS64(ehdr) \
? (phdr).class64. p_ ## field \
: (phdr).class32. p_ ## field)
/* Helper to access a @field of the structure DynamicEntryXX */
#define DYNAMIC_FIELD(ehdr, dynent, field) \
(IS_CLASS64(ehdr) \
? (dynent).class64. d_ ## field \
: (dynent).class32. d_ ## field)
#define KNOWN_PHENTSIZE(header, size) \
( (IS_CLASS32(header) && (size) == sizeof(ProgramHeader32)) \
|| (IS_CLASS64(header) && (size) == sizeof(ProgramHeader64)))
#define IS_POSITION_INDENPENDANT(elf_header) \
(ELF_FIELD((elf_header), type) == ET_DYN)
#include "tracee/tracee.h"
extern int open_elf(const char *t_path, ElfHeader *elf_header);
typedef int (* program_headers_iterator_t)(const ElfHeader *elf_header,
const ProgramHeader *program_header, void *data);
extern int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header,
program_headers_iterator_t callback, void *data);
#endif /* ELF_H */
+464
View File
@@ -0,0 +1,464 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/types.h> /* lstat(2), lseek(2), */
#include <sys/stat.h> /* lstat(2), lseek(2), fchmod(2), */
#include <unistd.h> /* access(2), lstat(2), close(2), read(2), */
#include <errno.h> /* E*, */
#include <assert.h> /* assert(3), */
#include <talloc.h> /* talloc*, */
#include <sys/mman.h> /* PROT_*, */
#include <string.h> /* strlen(3), strcpy(3), */
#include <stdlib.h> /* getenv(3), */
#include <stdio.h> /* fwrite(3), */
#include <assert.h> /* assert(3), */
#include "execve/execve.h"
#include "execve/shebang.h"
#include "execve/aoxp.h"
#include "execve/elf.h"
#include "path/path.h"
#include "path/temp.h"
#include "path/binding.h"
#include "tracee/tracee.h"
#include "syscall/syscall.h"
#include "syscall/sysnum.h"
#include "arch.h"
#include "cli/note.h"
#define P(a) PROGRAM_FIELD(load_info->elf_header, *program_header, a)
/**
* Add @program_header (type PT_LOAD) to @load_info->mappings. This
* function returns -errno if an error occured, otherwise it returns
* 0.
*/
static int add_mapping(const Tracee *tracee UNUSED, LoadInfo *load_info,
const ProgramHeader *program_header)
{
size_t index;
word_t start_address;
word_t end_address;
static word_t page_size = 0;
static word_t page_mask = 0;
if (page_size == 0) {
page_size = sysconf(_SC_PAGE_SIZE);
if ((int) page_size <= 0)
page_size = 0x1000;
page_mask = ~(page_size - 1);
}
if (load_info->mappings == NULL)
index = 0;
else
index = talloc_array_length(load_info->mappings);
load_info->mappings = talloc_realloc(load_info, load_info->mappings, Mapping, index + 1);
if (load_info->mappings == NULL)
return -ENOMEM;
start_address = P(vaddr) & page_mask;
end_address = (P(vaddr) + P(filesz) + page_size) & page_mask;
load_info->mappings[index].fd = -1; /* Unknown yet. */
load_info->mappings[index].offset = P(offset) & page_mask;
load_info->mappings[index].addr = start_address;
load_info->mappings[index].length = end_address - start_address;
load_info->mappings[index].flags = MAP_PRIVATE | MAP_FIXED;
load_info->mappings[index].prot = ( (P(flags) & PF_R ? PROT_READ : 0)
| (P(flags) & PF_W ? PROT_WRITE : 0)
| (P(flags) & PF_X ? PROT_EXEC : 0));
/* "If the segment's memory size p_memsz is larger than the
* file size p_filesz, the "extra" bytes are defined to hold
* the value 0 and to follow the segment's initialized area."
* -- man 7 elf. */
if (P(memsz) > P(filesz)) {
/* How many extra bytes in the current page? */
load_info->mappings[index].clear_length = end_address - P(vaddr) - P(filesz);
/* Create new pages for the remaining extra bytes. */
start_address = end_address;
end_address = (P(vaddr) + P(memsz) + page_size) & page_mask;
if (end_address > start_address) {
index++;
load_info->mappings = talloc_realloc(load_info, load_info->mappings,
Mapping, index + 1);
if (load_info->mappings == NULL)
return -ENOMEM;
load_info->mappings[index].fd = -1; /* Anonymous. */
load_info->mappings[index].offset = 0;
load_info->mappings[index].addr = start_address;
load_info->mappings[index].length = end_address - start_address;
load_info->mappings[index].clear_length = 0;
load_info->mappings[index].flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
load_info->mappings[index].prot = load_info->mappings[index - 1].prot;
}
}
else
load_info->mappings[index].clear_length = 0;
return 0;
}
/**
* Translate @user_path into @host_path and check if this latter exists, is
* executable and is a regular file. This function returns -errno if
* an error occured, 0 otherwise.
*/
int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path)
{
struct stat statl;
int status;
if (user_path[0] == '\0')
return -ENOEXEC;
status = translate_path(tracee, host_path, AT_FDCWD, user_path, true);
if (status < 0)
return status;
status = access(host_path, F_OK);
if (status < 0)
return -ENOENT;
status = access(host_path, X_OK);
if (status < 0)
return -EACCES;
status = lstat(host_path, &statl);
if (status < 0)
return -EPERM;
return 0;
}
/**
* Add @program_header (type PT_INTERP) to @load_info->interp. This
* function returns -errno if an error occured, otherwise it returns
* 0.
*/
static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info,
const ProgramHeader *program_header)
{
char host_path[PATH_MAX];
char *user_path;
int status;
/* Only one PT_INTERP segment is allowed. */
if (load_info->interp != NULL)
return -EINVAL;
load_info->interp = talloc_zero(load_info, LoadInfo);
if (load_info->interp == NULL)
return -ENOMEM;
user_path = talloc_size(tracee->ctx, P(filesz) + 1);
if (user_path == NULL)
return -ENOMEM;
/* Remember pread(2) doesn't change the
* current position in the file. */
status = pread(fd, user_path, P(filesz), P(offset));
if ((size_t) status != P(filesz)) /* Unexpected size. */
status = -EACCES;
if (status < 0)
return status;
user_path[P(filesz)] = '\0';
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
load_info->interp->host_path = talloc_strdup(load_info->interp, host_path);
if (load_info->interp->host_path == NULL)
return -ENOMEM;
load_info->interp->user_path = talloc_strdup(load_info->interp, user_path);
if (load_info->interp->user_path == NULL)
return -ENOMEM;
return 0;
}
#undef P
struct add_load_info_data {
LoadInfo *load_info;
Tracee *tracee;
int fd;
};
/**
* This function is a program header iterator. It invokes
* add_mapping() or add_interp(), according to the type of
* @program_header. This function returns -errno if an error
* occurred, otherwise 0.
*/
static int add_load_info(const ElfHeader *elf_header,
const ProgramHeader *program_header, void *data_)
{
struct add_load_info_data *data = data_;
int status;
switch (PROGRAM_FIELD(*elf_header, *program_header, type)) {
case PT_LOAD:
status = add_mapping(data->tracee, data->load_info, program_header);
if (status < 0)
return status;
break;
case PT_INTERP:
status = add_interp(data->tracee, data->fd, data->load_info, program_header);
if (status < 0)
return status;
break;
case PT_GNU_STACK:
data->load_info->needs_executable_stack |=
((PROGRAM_FIELD(*elf_header, *program_header, flags) & PF_X) != 0);
break;
default:
break;
}
return 0;
}
/**
* Extract the load info from @load->host_path. This function returns
* -errno if an error occured, otherwise it returns 0.
*/
static int extract_load_info(Tracee *tracee, LoadInfo *load_info)
{
struct add_load_info_data data;
int fd = -1;
int status;
assert(load_info != NULL);
assert(load_info->host_path != NULL);
fd = open_elf(load_info->host_path, &load_info->elf_header);
if (fd < 0)
return fd;
/* Sanity check. */
switch (ELF_FIELD(load_info->elf_header, type)) {
case ET_EXEC:
case ET_DYN:
break;
default:
status = -EINVAL;
goto end;
}
data.load_info = load_info;
data.tracee = tracee;
data.fd = fd;
status = iterate_program_headers(tracee, fd, &load_info->elf_header, add_load_info, &data);
end:
if (fd >= 0)
close(fd);
return status;
}
/**
* Add @load_base to each adresses of @load_info.
*/
static void add_load_base(LoadInfo *load_info, word_t load_base)
{
size_t nb_mappings;
size_t i;
nb_mappings = talloc_array_length(load_info->mappings);
for (i = 0; i < nb_mappings; i++)
load_info->mappings[i].addr += load_base;
if (IS_CLASS64(load_info->elf_header))
load_info->elf_header.class64.e_entry += load_base;
else
load_info->elf_header.class32.e_entry += load_base;
}
/**
* Compute the final load address for each position independant
* objects of @tracee.
*
*/
static void compute_load_addresses(Tracee *tracee)
{
if (IS_POSITION_INDENPENDANT(tracee->load_info->elf_header)
&& tracee->load_info->mappings[0].addr == 0) {
#if defined(HAS_LOADER_32BIT)
if (IS_CLASS32(tracee->load_info->elf_header))
add_load_base(tracee->load_info, EXEC_PIC_ADDRESS_32);
else
#endif
add_load_base(tracee->load_info, EXEC_PIC_ADDRESS);
}
/* Nothing more to do? */
if (tracee->load_info->interp == NULL)
return;
if (IS_POSITION_INDENPENDANT(tracee->load_info->interp->elf_header)
&& tracee->load_info->interp->mappings[0].addr == 0) {
#if defined(HAS_LOADER_32BIT)
if (IS_CLASS32(tracee->load_info->elf_header))
add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS_32);
else
#endif
add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS);
}
}
/**
* Get the path to the loader for the given @tracee. This function
* returns NULL if an error occurred.
*/
static inline const char *get_loader_path(const Tracee *tracee)
{
static char *loader_path = NULL;
if (IS_CLASS32(tracee->load_info->elf_header))
loader_path = getenv("PROOT_LOADER_32");
else
loader_path = getenv("PROOT_LOADER");
return loader_path;
}
/**
* Extract all the information that will be required by
* translate_load_*(). This function returns -errno if an error
* occured, otherwise 0.
*/
int translate_execve_enter(Tracee *tracee)
{
char user_path[PATH_MAX];
char host_path[PATH_MAX];
char new_exe[PATH_MAX];
char *raw_path;
const char *loader_path;
int status;
if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) {
/* Syscalls can now be reported to its ptracer. */
tracee->as_ptracee.ignore_loader_syscalls = false;
/* Cancel this spurious execve, it was only used as a
* notification. */
set_sysnum(tracee, PR_void);
return 0;
}
status = get_sysarg_path(tracee, user_path, SYSARG_1);
if (status < 0)
return status;
/* Remember the user path before it is overwritten by
* expand_shebang(). This "raw" path is useful to fix the
* value of AT_EXECFN and /proc/{@tracee->pid}/comm. */
raw_path = talloc_strdup(tracee->ctx, user_path);
if (raw_path == NULL)
return -ENOMEM;
status = expand_shebang(tracee, host_path, user_path);
if (status < 0)
/* The Linux kernel actually returns -EACCES when
* trying to execute a directory. */
return status == -EISDIR ? -EACCES : status;
/* user_path is modified only if there's an interpreter
* (ie. for a script). */
if (status == 0)
TALLOC_FREE(raw_path);
strcpy(new_exe, host_path);
status = detranslate_path(tracee, new_exe, NULL);
if (status >= 0) {
talloc_unlink(tracee, tracee->new_exe);
tracee->new_exe = talloc_strdup(tracee, new_exe);
}
else
tracee->new_exe = NULL;
talloc_unlink(tracee, tracee->load_info);
tracee->load_info = talloc_zero(tracee, LoadInfo);
if (tracee->load_info == NULL)
return -ENOMEM;
tracee->load_info->host_path = talloc_strdup(tracee->load_info, host_path);
if (tracee->load_info->host_path == NULL)
return -ENOMEM;
tracee->load_info->user_path = talloc_strdup(tracee->load_info, user_path);
if (tracee->load_info->user_path == NULL)
return -ENOMEM;
tracee->load_info->raw_path = (raw_path != NULL
? talloc_reparent(tracee->ctx, tracee->load_info, raw_path)
: talloc_reference(tracee->load_info, tracee->load_info->user_path));
if (tracee->load_info->raw_path == NULL)
return -ENOMEM;
status = extract_load_info(tracee, tracee->load_info);
if (status < 0)
return status;
if (tracee->load_info->interp != NULL) {
status = extract_load_info(tracee, tracee->load_info->interp);
if (status < 0)
return status;
/* An ELF interpreter is supposed to be
* standalone. */
if (tracee->load_info->interp->interp != NULL) {
TALLOC_FREE(tracee->load_info->interp->interp);
return -EINVAL;
}
}
compute_load_addresses(tracee);
/* Execute the loader instead of the program. */
loader_path = get_loader_path(tracee);
if (loader_path == NULL)
return -ENOENT;
status = set_sysarg_path(tracee, loader_path, SYSARG_1);
if (status < 0)
return status;
/* Mask to its ptracer syscalls performed by the loader. */
tracee->as_ptracee.ignore_loader_syscalls = true;
return 0;
}
@@ -0,0 +1,64 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef EXECVE_H
#define EXECVE_H
#include <linux/limits.h> /* PATH_MAX, */
#include "tracee/tracee.h"
#include "execve/elf.h"
#include "arch.h"
extern int translate_execve_enter(Tracee *tracee);
extern void translate_execve_exit(Tracee *tracee);
extern int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path);
typedef struct mapping {
word_t addr;
word_t length;
word_t clear_length;
word_t prot;
word_t flags;
word_t fd;
word_t offset;
} Mapping;
typedef struct load_info {
char *host_path;
char *user_path;
char *raw_path;
Mapping *mappings;
ElfHeader elf_header;
bool needs_executable_stack;
struct load_info *interp;
} LoadInfo;
#define IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee) ( \
(tracee)->as_ptracee.ptracer != NULL \
&& peek_reg((tracee), ORIGINAL, SYSARG_1) == (word_t) 1 \
&& peek_reg((tracee), ORIGINAL, SYSARG_4) == (word_t) 2 \
&& peek_reg((tracee), ORIGINAL, SYSARG_5) == (word_t) 3 \
&& peek_reg((tracee), ORIGINAL, SYSARG_6) == (word_t) 4)
#endif /* EXECVE_H */
+487
View File
@@ -0,0 +1,487 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <linux/auxvec.h> /* AT_*, */
#include <talloc.h> /* talloc*, */
#include <sys/mman.h> /* MAP_*, */
#include <assert.h> /* assert(3), */
#include <string.h> /* strlen(3), strerror(3), */
#include <strings.h> /* bzero(3), */
#include <signal.h> /* kill(2), SIG*, */
#include <unistd.h> /* write(2), */
#include <errno.h> /* E*, */
#include "execve/execve.h"
#include "execve/elf.h"
#include "loader/script.h"
#include "tracee/reg.h"
#include "tracee/abi.h"
#include "tracee/mem.h"
#include "syscall/sysnum.h"
#include "execve/auxv.h"
#include "path/binding.h"
#include "path/temp.h"
#include "cli/note.h"
/**
* Fill @path with the content of @vectors, formatted according to
* @ptracee's current ABI.
*/
static int fill_file_with_auxv(const Tracee *ptracee, const char *path,
const ElfAuxVector *vectors)
{
const ssize_t current_sizeof_word = sizeof_word(ptracee);
ssize_t status;
int fd = -1;
int i;
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
i = 0;
do {
status = write(fd, &vectors[i].type, current_sizeof_word);
if (status < current_sizeof_word) {
status = -1;
goto end;
}
status = write(fd, &vectors[i].value, current_sizeof_word);
if (status < current_sizeof_word) {
status = -1;
goto end;
}
} while (vectors[i++].type != AT_NULL);
status = 0;
end:
if (fd >= 0)
(void) close(fd);
return status;
}
/**
* Bind content of @vectors over /proc/{@ptracee->pid}/auxv. This
* function returns -1 if an error occurred, otherwise 0.
*/
static int bind_proc_pid_auxv(const Tracee *ptracee)
{
word_t vectors_address;
ElfAuxVector *vectors;
const char *guest_path;
const char *host_path;
Binding *binding;
int status;
vectors_address = get_elf_aux_vectors_address(ptracee);
if (vectors_address == 0)
return -1;
vectors = fetch_elf_aux_vectors(ptracee, vectors_address);
if (vectors == NULL)
return -1;
/* Path to these ELF auxiliary vectors. */
guest_path = talloc_asprintf(ptracee->ctx, "/proc/%d/auxv", ptracee->pid);
if (guest_path == NULL)
return -1;
/* Remove binding to this path, if any. It contains ELF
* auxiliary vectors of the previous execve(2). */
binding = get_binding(ptracee, GUEST, guest_path);
if (binding != NULL && compare_paths(binding->guest.path, guest_path) == PATHS_ARE_EQUAL) {
remove_binding_from_all_lists(ptracee, binding);
TALLOC_FREE(binding);
}
host_path = create_temp_file(ptracee->ctx, "auxv");
if (host_path == NULL)
return -1;
status = fill_file_with_auxv(ptracee, host_path, vectors);
if (status < 0)
return -1;
/* Note: this binding will be removed once ptracee gets freed. */
binding = insort_binding3(ptracee, ptracee->life_context, host_path, guest_path);
if (binding == NULL)
return -1;
/* This temporary file (host_path) will be removed once the
* binding is freed. */
talloc_reparent(ptracee->ctx, binding, host_path);
return 0;
}
/**
* Convert @mappings into load @script statements at the given @cursor
* position. This function returns the new cursor position.
*/
static void *transcript_mappings(void *cursor, const Mapping *mappings)
{
size_t nb_mappings;
size_t i;
nb_mappings = talloc_array_length(mappings);
for (i = 0; i < nb_mappings; i++) {
LoadStatement *statement = cursor;
if ((mappings[i].flags & MAP_ANONYMOUS) != 0)
statement->action = LOAD_ACTION_MMAP_ANON;
else
statement->action = LOAD_ACTION_MMAP_FILE;
statement->mmap.addr = mappings[i].addr;
statement->mmap.length = mappings[i].length;
statement->mmap.prot = mappings[i].prot;
statement->mmap.offset = mappings[i].offset;
statement->mmap.clear_length = mappings[i].clear_length;
cursor += LOAD_STATEMENT_SIZE(*statement, mmap);
}
return cursor;
}
/**
* Convert @tracee->load_info into a load script, then transfer this
* latter into @tracee's memory.
*/
static int transfer_load_script(Tracee *tracee)
{
const word_t stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER);
static word_t page_size = 0;
static word_t page_mask = 0;
word_t entry_point;
size_t script_size;
size_t strings_size;
size_t string1_size;
size_t string2_size;
size_t string3_size;
size_t padding_size;
word_t string1_address;
word_t string2_address;
word_t string3_address;
void *buffer;
size_t buffer_size;
bool needs_executable_stack;
LoadStatement *statement;
void *cursor;
int status;
if (page_size == 0) {
page_size = sysconf(_SC_PAGE_SIZE);
if ((int) page_size <= 0)
page_size = 0x1000;
page_mask = ~(page_size - 1);
}
needs_executable_stack = (tracee->load_info->needs_executable_stack
|| ( tracee->load_info->interp != NULL
&& tracee->load_info->interp->needs_executable_stack));
/* Strings addresses are required to generate the load script,
* for "open" actions. Since I want to generate it in one
* pass, these strings will be put right below the current
* stack pointer -- the only known adresses so far -- in the
* "strings area". */
string1_size = strlen(tracee->load_info->user_path) + 1;
string2_size = (tracee->load_info->interp == NULL ? 0
: strlen(tracee->load_info->interp->user_path) + 1);
string3_size = (tracee->load_info->raw_path == tracee->load_info->user_path ? 0
: strlen(tracee->load_info->raw_path) + 1);
/* A padding will be appended at the end of the load script
* (a.k.a "strings area") to ensure this latter is aligned properly. */
padding_size = (stack_pointer - string1_size - string2_size - string3_size)
% STACK_ALIGNMENT;
strings_size = string1_size + string2_size + string3_size + padding_size;
string1_address = stack_pointer - strings_size;
string2_address = stack_pointer - strings_size + string1_size;
string3_address = (string3_size == 0
? string1_address
: stack_pointer - strings_size + string1_size + string2_size);
/* Compute the size of the load script. */
script_size =
LOAD_STATEMENT_SIZE(*statement, open)
+ (LOAD_STATEMENT_SIZE(*statement, mmap)
* talloc_array_length(tracee->load_info->mappings))
+ (tracee->load_info->interp == NULL ? 0
: LOAD_STATEMENT_SIZE(*statement, open)
+ (LOAD_STATEMENT_SIZE(*statement, mmap)
* talloc_array_length(tracee->load_info->interp->mappings)))
+ (needs_executable_stack ? LOAD_STATEMENT_SIZE(*statement, make_stack_exec) : 0)
+ LOAD_STATEMENT_SIZE(*statement, start);
/* Allocate enough room for both the load script and the
* strings area. */
buffer_size = script_size + strings_size;
buffer = talloc_zero_size(tracee->ctx, buffer_size);
if (buffer == NULL)
return -ENOMEM;
cursor = buffer;
/* Load script statement: open. */
statement = cursor;
statement->action = LOAD_ACTION_OPEN;
statement->open.string_address = string1_address;
cursor += LOAD_STATEMENT_SIZE(*statement, open);
/* Load script statements: mmap. */
cursor = transcript_mappings(cursor, tracee->load_info->mappings);
if (tracee->load_info->interp != NULL) {
/* Load script statement: open. */
statement = cursor;
statement->action = LOAD_ACTION_OPEN_NEXT;
statement->open.string_address = string2_address;
cursor += LOAD_STATEMENT_SIZE(*statement, open);
/* Load script statements: mmap. */
cursor = transcript_mappings(cursor, tracee->load_info->interp->mappings);
entry_point = ELF_FIELD(tracee->load_info->interp->elf_header, entry);
}
else
entry_point = ELF_FIELD(tracee->load_info->elf_header, entry);
if (needs_executable_stack) {
/* Load script statement: stack_exec. */
statement = cursor;
statement->action = LOAD_ACTION_MAKE_STACK_EXEC;
statement->make_stack_exec.start = stack_pointer & page_mask;
cursor += LOAD_STATEMENT_SIZE(*statement, make_stack_exec);
}
/* Load script statement: start. */
statement = cursor;
/* Start of the program slightly differs when ptraced. */
if (tracee->as_ptracee.ptracer != NULL)
statement->action = LOAD_ACTION_START_TRACED;
else
statement->action = LOAD_ACTION_START;
statement->start.stack_pointer = stack_pointer;
statement->start.entry_point = entry_point;
statement->start.at_phent = ELF_FIELD(tracee->load_info->elf_header, phentsize);
statement->start.at_phnum = ELF_FIELD(tracee->load_info->elf_header, phnum);
statement->start.at_entry = ELF_FIELD(tracee->load_info->elf_header, entry);
statement->start.at_phdr = ELF_FIELD(tracee->load_info->elf_header, phoff)
+ tracee->load_info->mappings[0].addr;
statement->start.at_execfn = string3_address;
cursor += LOAD_STATEMENT_SIZE(*statement, start);
/* Sanity check. */
assert((uintptr_t) cursor - (uintptr_t) buffer == script_size);
/* Convert the load script to the expected format. */
if (is_32on64_mode(tracee)) {
int i;
for (i = 0; buffer + i * sizeof(uint64_t) < cursor; i++)
((uint32_t *) buffer)[i] = ((uint64_t *) buffer)[i];
}
/* Concatenate the load script and the strings. */
memcpy(cursor, tracee->load_info->user_path, string1_size);
cursor += string1_size;
if (string2_size != 0) {
memcpy(cursor, tracee->load_info->interp->user_path, string2_size);
cursor += string2_size;
}
if (string3_size != 0) {
memcpy(cursor, tracee->load_info->raw_path, string3_size);
cursor += string3_size;
}
/* Sanity check. */
cursor += padding_size;
assert((uintptr_t) cursor - (uintptr_t) buffer == buffer_size);
/* Allocate enough room in tracee's memory for the load
* script, and make the first user argument points to this
* location. Note that it is safe to update the stack pointer
* manually since we are in execve sysexit. However it should
* be done before transfering data since the kernel might not
* allow page faults below the stack pointer. */
poke_reg(tracee, STACK_POINTER, stack_pointer - buffer_size);
poke_reg(tracee, USERARG_1, stack_pointer - buffer_size);
/* Copy everything in the tracee's memory at once. */
status = write_data(tracee, stack_pointer - buffer_size, buffer, buffer_size);
if (status < 0)
return status;
/* Tracee's stack content is now as follow:
*
* +------------+ <- initial stack pointer (higher address)
* | padding |
* +------------+
* | string3 |
* +------------+
* | string2 |
* +------------+
* | string1 |
* +------------+
* | start |
* +------------+
* | mmap anon |
* +------------+
* | mmap file |
* +------------+
* | open next |
* +------------+
* | mmap anon. |
* +------------+
* | mmap file |
* +------------+
* | open |
* +------------+ <- stack pointer, userarg1 (word aligned)
*/
/* Remember we are in the sysexit stage, so be sure the
* current register values will be used as-is at the end. */
save_current_regs(tracee, ORIGINAL);
tracee->_regs_were_changed = true;
return 0;
}
/**
* Start the loading of @tracee. This function returns no error since
* it's either too late to do anything useful (the calling process is
* already replaced) or the error reported by the kernel
* (syscall_result < 0) will be propagated as-is.
*/
void translate_execve_exit(Tracee *tracee)
{
word_t syscall_result;
int status;
if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) {
/* Be sure not to confuse the ptracer with an
* unexpected syscall/returned value. */
poke_reg(tracee, SYSARG_RESULT, 0);
set_sysnum(tracee, PR_execve);
/* According to most ABIs, all registers have
* undefined values at program startup except:
*
* - the stack pointer
* - the instruction pointer
* - the rtld_fini pointer
* - the state flags
*/
poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_2));
poke_reg(tracee, INSTR_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_3));
poke_reg(tracee, RTLD_FINI, 0);
poke_reg(tracee, STATE_FLAGS, 0);
#if defined(ARCH_ARM_EABI) && defined(__thumb__)
/* Leave ARM thumb mode */
tracee->_regs[CURRENT].ARM_cpsr &= ~PSR_T_BIT;
#endif
/* Restore registers to their current values. */
save_current_regs(tracee, ORIGINAL);
tracee->_regs_were_changed = true;
/* This is is required to make GDB work correctly
* under PRoot, however it deserves to be used
* unconditionally. */
(void) bind_proc_pid_auxv(tracee);
/* If the PTRACE_O_TRACEEXEC option is *not* in effect
* for the execing tracee, the kernel delivers an
* extra SIGTRAP to the tracee after execve(2)
* *returns*. This is an ordinary signal (similar to
* one which can be generated by "kill -TRAP"), not a
* special kind of ptrace-stop. Employing
* PTRACE_GETSIGINFO for this signal returns si_code
* set to 0 (SI_USER). This signal may be blocked by
* signal mask, and thus may be delivered (much)
* later. -- man 2 ptrace
*
* This signal is delayed so far since the program was
* not fully loaded yet; GDB would get "invalid
* adress" errors otherwise. */
if ((tracee->as_ptracee.options & PTRACE_O_TRACEEXEC) == 0)
kill(tracee->pid, SIGTRAP);
return;
}
#ifdef ARCH_ARM64
tracee->is_aarch32 = IS_CLASS32(tracee->load_info->elf_header);
#endif
syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
if ((int) syscall_result < 0)
return;
/* Execve happened; commit the new "/proc/self/exe". */
if (tracee->new_exe != NULL) {
(void) talloc_unlink(tracee, tracee->exe);
tracee->exe = talloc_reference(tracee, tracee->new_exe);
talloc_set_name_const(tracee->exe, "$exe");
}
/* New processes have no heap. The process could've been cloned with
* CLONE_VM so it has been sharing the heap with its parent. execve()
* discards the VM so make sure to reallocate new heap. */
if (talloc_reference_count(tracee->heap) > 0) {
talloc_unlink(tracee, tracee->heap);
tracee->heap = talloc_zero(tracee, Heap);
if (!tracee->heap)
note(tracee, ERROR, INTERNAL, "can't allocate heap");
} else {
bzero(tracee->heap, sizeof(Heap));
}
status = transfer_load_script(tracee);
if (status < 0)
note(tracee, ERROR, INTERNAL, "can't transfer load script: %s", strerror(-status));
return;
}
+307
View File
@@ -0,0 +1,307 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/types.h> /* open(2), */
#include <sys/stat.h> /* open(2), */
#include <fcntl.h> /* open(2), */
#include <linux/limits.h> /* PATH_MAX, */
#include <linux/binfmts.h> /* BINPRM_BUF_SIZE, */
#include <unistd.h> /* read(2), close(2), */
#include <errno.h> /* -E*, */
#include <sys/param.h> /* MAXSYMLINKS, */
#include <stdbool.h> /* bool, */
#include <assert.h> /* assert(3), */
#include "execve/shebang.h"
#include "execve/execve.h"
#include "execve/aoxp.h"
#include "tracee/tracee.h"
#include "attribute.h"
/**
* Extract into @user_path and @argument the shebang from @host_path.
* This function returns -errno if an error occured, 1 if a shebang
* was found and extracted, otherwise 0.
*
* Extract from "man 2 execve":
*
* On Linux, the entire string following the interpreter name is
* passed as a *single* argument to the interpreter, and this
* string can include white space.
*/
static int extract_shebang(const Tracee *tracee UNUSED, const char *host_path,
char user_path[PATH_MAX], char argument[BINPRM_BUF_SIZE])
{
char tmp2[2];
char tmp;
size_t current_length;
size_t i;
int status;
int fd;
/* Assumption. */
assert(BINPRM_BUF_SIZE < PATH_MAX);
argument[0] = '\0';
/* Inspect the executable. */
fd = open(host_path, O_RDONLY);
if (fd < 0)
return -errno;
status = read(fd, tmp2, 2 * sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < 2 * sizeof(char)) { /* EOF */
status = 0;
goto end;
}
/* Check if it really is a script text. */
if (tmp2[0] != '#' || tmp2[1] != '!') {
status = 0;
goto end;
}
current_length = 2;
user_path[0] = '\0';
/* Skip leading spaces. */
do {
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
status = -ENOEXEC;
goto end;
}
current_length++;
} while ((tmp == ' ' || tmp == '\t') && current_length < BINPRM_BUF_SIZE);
/* Slurp the interpreter path until the first space or end-of-line. */
for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) {
switch (tmp) {
case ' ':
case '\t':
/* Remove spaces in between the interpreter
* and the hypothetical argument. */
user_path[i] = '\0';
break;
case '\n':
case '\r':
/* There is no argument. */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
default:
/* There is an argument if the previous
* character in user_path[] is '\0'. */
if (i > 1 && user_path[i - 1] == '\0')
goto argument;
else
user_path[i] = tmp;
break;
}
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
}
}
/* The interpreter path is too long, truncate it. */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
argument:
/* Slurp the argument until the end-of-line. */
for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) {
switch (tmp) {
case '\n':
case '\r':
argument[i] = '\0';
/* Remove trailing spaces. */
for (i--; i > 0 && (argument[i] == ' ' || argument[i] == '\t'); i--)
argument[i] = '\0';
status = 1;
goto end;
default:
argument[i] = tmp;
break;
}
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
argument[0] = '\0';
status = 1;
goto end;
}
}
/* The argument is too long, truncate it. */
argument[i] = '\0';
status = 1;
end:
close(fd);
/* Did an error occur or isn't a script? */
if (status <= 0)
return status;
return 1;
}
/**
* Expand in argv[] the shebang of @user_path, if any. This function
* returns -errno if an error occurred, 1 if a shebang was found and
* extracted, otherwise 0. On success, both @host_path and @user_path
* point to the program to execute (respectively from host
* point-of-view and as-is), and @tracee's argv[] (pointed to by
* SYSARG_2) is correctly updated.
*/
int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX])
{
ArrayOfXPointers *argv = NULL;
bool has_shebang = false;
char argument[BINPRM_BUF_SIZE];
int status;
size_t i;
/* "The interpreter must be a valid pathname for an executable
* which is not itself a script [1]. If the filename
* argument of execve() specifies an interpreter script, then
* interpreter will be invoked with the following arguments:
*
* interpreter [optional-arg] filename arg...
*
* where arg... is the series of words pointed to by the argv
* argument of execve()." -- man 2 execve
*
* [1]: as of this writing (3.10.17) this is true only for the
* ELF interpreter; ie. a script can use a script as
* interpreter.
*/
for (i = 0; i < MAXSYMLINKS; i++) {
char *old_user_path;
/* Translate this path (user -> host), then check it is executable. */
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
/* Remember the initial user path. */
old_user_path = talloc_strdup(tracee->ctx, user_path);
if (old_user_path == NULL)
return -ENOMEM;
/* Extract into user_path and argument the shebang from host_path. */
status = extract_shebang(tracee, host_path, user_path, argument);
if (status < 0)
return status;
/* No more shebang. */
if (status == 0)
break;
has_shebang = true;
/* Translate new path (user -> host), then check it is executable. */
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
/* Fetch argv[] only on demand. */
if (argv == NULL) {
status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0);
if (status < 0)
return status;
}
/* Assuming the shebang of "script" is "#!/bin/sh -x",
* a call to:
*
* execve("./script", { "script.sh", NULL }, ...)
*
* becomes:
*
* execve("/bin/sh", { "/bin/sh", "-x", "./script", NULL }, ...)
*
* See commit 8c8fbe85 about "argv->length == 1". */
if (argument[0] != '\0') {
status = resize_array_of_xpointers(argv, 0, 2 + (argv->length == 1));
if (status < 0)
return status;
status = write_xpointees(argv, 0, 3, user_path, argument, old_user_path);
if (status < 0)
return status;
}
else {
status = resize_array_of_xpointers(argv, 0, 1 + (argv->length == 1));
if (status < 0)
return status;
status = write_xpointees(argv, 0, 2, user_path, old_user_path);
if (status < 0)
return status;
}
}
if (i == MAXSYMLINKS)
return -ELOOP;
/* Push argv[] only on demand. */
if (argv != NULL) {
status = push_array_of_xpointers(argv, SYSARG_2);
if (status < 0)
return status;
}
return (has_shebang ? 1 : 0);
}
@@ -0,0 +1,32 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SHEBANG_H
#define SHEBANG_H
#include <linux/limits.h> /* PATH_MAX, ARG_MAX, */
#include "tracee/tracee.h"
extern int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]);
#endif /* SHEBANG_H */
@@ -0,0 +1,111 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/* According to the ARM EABI, all registers have undefined values at
* program startup except:
*
* - the instruction pointer (r15)
* - the stack pointer (r13)
* - the rtld_fini pointer (r0)
*/
#define BRANCH(stack_pointer, destination) do { \
asm volatile ( \
"// Restore initial stack pointer. \n\t" \
"mov sp, %0 \n\t" \
" \n\t" \
"// Clear rtld_fini. \n\t" \
"mov r0, #0 \n\t" \
" \n\t" \
"// Start the program. \n\t" \
"bx %1 \n" \
: /* no output */ \
: "r" (stack_pointer), "r" (destination) \
: "memory", "sp", "r0", "pc"); \
__builtin_unreachable(); \
} while (0)
#define PREPARE_ARGS_1(arg1_) \
register word_t arg1 asm("r0") = arg1_; \
#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \
PREPARE_ARGS_1(arg1_) \
register word_t arg2 asm("r1") = arg2_; \
register word_t arg3 asm("r2") = arg3_; \
#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \
PREPARE_ARGS_3(arg1_, arg2_, arg3_) \
register word_t arg4 asm("r3") = arg4_; \
register word_t arg5 asm("r4") = arg5_; \
register word_t arg6 asm("r5") = arg6_;
#define OUTPUT_CONTRAINTS_1 \
"r" (arg1)
#define OUTPUT_CONTRAINTS_3 \
OUTPUT_CONTRAINTS_1, \
"r" (arg2), "r" (arg3)
#define OUTPUT_CONTRAINTS_6 \
OUTPUT_CONTRAINTS_3, \
"r" (arg4), "r" (arg5), "r" (arg6)
#ifdef __thumb__
#define STRINGIFY_EXPANDED(s) #s
#define SYSCALL(number_, nb_args, args...) \
({ \
register word_t result asm("r0"); \
PREPARE_ARGS_##nb_args(args) \
asm volatile ( \
"push {r7} \n\t" \
"mov r7, #" STRINGIFY_EXPANDED(number_) "\n\t" \
"svc #0x00000000 \n\t" \
"pop {r7} \n\t" \
: "=r" (result) \
: OUTPUT_CONTRAINTS_##nb_args \
: "memory"); \
result; \
})
#else
#define SYSCALL(number_, nb_args, args...) \
({ \
register word_t number asm("r7") = number_; \
register word_t result asm("r0"); \
PREPARE_ARGS_##nb_args(args) \
asm volatile ( \
"svc #0x00000000 \n\t" \
: "=r" (result) \
: "r" (number), \
OUTPUT_CONTRAINTS_##nb_args \
: "memory"); \
result; \
})
#endif
#define OPEN 5
#define CLOSE 6
#define MMAP 192
#define MMAP_OFFSET_SHIFT 12
#define EXECVE 11
#define EXIT 1
#define PRCTL 172
#define MPROTECT 125
@@ -0,0 +1,98 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/* According to the ARM64 EABI, all registers have undefined values at
* program startup except:
*
* - the instruction pointer (pc)
* - the stack pointer (sp)
* - the rtld_fini pointer (x0)
*/
#define BRANCH(stack_pointer, destination) do { \
asm volatile ( \
"// Restore initial stack pointer. \n\t" \
"mov sp, %0 \n\t" \
" \n\t" \
"// Clear rtld_fini. \n\t" \
"mov x0, #0 \n\t" \
" \n\t" \
"// Start the program. \n\t" \
"br %1 \n" \
: /* no output */ \
: "r" (stack_pointer), "r" (destination) \
: "memory", "sp", "x0"); \
__builtin_unreachable(); \
} while (0)
#define PREPARE_ARGS_1(arg1_) \
register word_t arg1 asm("x0") = arg1_; \
#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \
PREPARE_ARGS_1(arg1_) \
register word_t arg2 asm("x1") = arg2_; \
register word_t arg3 asm("x2") = arg3_; \
#define PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \
PREPARE_ARGS_3(arg1_, arg2_, arg3_) \
register word_t arg4 asm("x3") = arg4_; \
#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \
PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \
register word_t arg5 asm("x4") = arg5_; \
register word_t arg6 asm("x5") = arg6_;
#define OUTPUT_CONTRAINTS_1 \
"r" (arg1)
#define OUTPUT_CONTRAINTS_3 \
OUTPUT_CONTRAINTS_1, \
"r" (arg2), "r" (arg3)
#define OUTPUT_CONTRAINTS_4 \
OUTPUT_CONTRAINTS_3, \
"r" (arg4)
#define OUTPUT_CONTRAINTS_6 \
OUTPUT_CONTRAINTS_4, \
"r" (arg5), "r" (arg6)
#define SYSCALL(number_, nb_args, args...) \
({ \
register word_t number asm("x8") = number_; \
register word_t result asm("x0"); \
PREPARE_ARGS_##nb_args(args) \
asm volatile ( \
"svc #0x00000000 \n\t" \
: "=r" (result) \
: "r" (number), \
OUTPUT_CONTRAINTS_##nb_args \
: "memory"); \
result; \
})
#define OPENAT 56
#define CLOSE 57
#define MMAP 222
#define EXECVE 221
#define EXIT 93
#define PRCTL 167
#define MPROTECT 226
+259
View File
@@ -0,0 +1,259 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stdbool.h> /* bool, true, false, */
#define NO_LIBC_HEADER
#include "loader/script.h"
#include "compat.h"
#include "arch.h"
#if defined(ARCH_ARM_EABI)
#include "loader/assembly-arm.h"
#elif defined(ARCH_ARM64)
#include "loader/assembly-arm64.h"
#else
#error "Unsupported architecture"
#endif
#if !defined(MMAP_OFFSET_SHIFT)
#define MMAP_OFFSET_SHIFT 0
#endif
#define FATAL() do { \
SYSCALL(EXIT, 1, 182); \
__builtin_unreachable(); \
} while (0)
#define unlikely(expr) __builtin_expect(!!(expr), 0)
/**
* Clear the memory from @start (inclusive) to @end (exclusive).
*/
static inline void clear(word_t start, word_t end)
{
byte_t *start_misaligned;
byte_t *end_misaligned;
word_t *start_aligned;
word_t *end_aligned;
/* Compute the number of mis-aligned bytes. */
word_t start_bytes = start % sizeof(word_t);
word_t end_bytes = end % sizeof(word_t);
/* Compute aligned addresses. */
start_aligned = (word_t *) (start_bytes ? start + sizeof(word_t) - start_bytes : start);
end_aligned = (word_t *) (end - end_bytes);
/* Clear leading mis-aligned bytes. */
start_misaligned = (byte_t *) start;
while (start_misaligned < (byte_t *) start_aligned)
*start_misaligned++ = 0;
/* Clear aligned bytes. */
while (start_aligned < end_aligned)
*start_aligned++ = 0;
/* Clear trailing mis-aligned bytes. */
end_misaligned = (byte_t *) end_aligned;
while (end_misaligned < (byte_t *) end)
*end_misaligned++ = 0;
}
/**
* Return the address of the last path component of @string_. Note
* that @string_ is not modified.
*/
static inline word_t basename(word_t string_)
{
byte_t *string = (byte_t *) string_;
byte_t *cursor;
for (cursor = string; *cursor != 0; cursor++)
;
for (; *cursor != (byte_t) '/' && cursor > string; cursor--)
;
if (cursor != string)
cursor++;
return (word_t) cursor;
}
/**
* Interpret the load script pointed to by @cursor.
*/
void _start(void *cursor)
{
bool traced = false;
bool reset_at_base = true;
word_t at_base = 0;
word_t fd = -1;
word_t status;
while(1) {
LoadStatement *stmt = cursor;
switch (stmt->action) {
case LOAD_ACTION_OPEN_NEXT:
status = SYSCALL(CLOSE, 1, fd);
if (unlikely((int) status < 0))
FATAL();
/* Fall through. */
case LOAD_ACTION_OPEN:
#if defined(OPEN)
fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0);
#else
fd = SYSCALL(OPENAT, 4, AT_FDCWD, stmt->open.string_address, O_RDONLY, 0);
#endif
if (unlikely((int) fd < 0))
FATAL();
reset_at_base = true;
cursor += LOAD_STATEMENT_SIZE(*stmt, open);
break;
case LOAD_ACTION_MMAP_FILE:
status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length,
stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED, fd,
stmt->mmap.offset >> MMAP_OFFSET_SHIFT);
if (unlikely(status != stmt->mmap.addr))
FATAL();
if (stmt->mmap.clear_length != 0)
clear(stmt->mmap.addr + stmt->mmap.length - stmt->mmap.clear_length,
stmt->mmap.addr + stmt->mmap.length);
if (reset_at_base) {
at_base = stmt->mmap.addr;
reset_at_base = false;
}
cursor += LOAD_STATEMENT_SIZE(*stmt, mmap);
break;
case LOAD_ACTION_MMAP_ANON:
status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length,
stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
if (unlikely(status != stmt->mmap.addr))
FATAL();
cursor += LOAD_STATEMENT_SIZE(*stmt, mmap);
break;
case LOAD_ACTION_MAKE_STACK_EXEC:
SYSCALL(MPROTECT, 3,
stmt->make_stack_exec.start, 1,
PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GROWSDOWN);
cursor += LOAD_STATEMENT_SIZE(*stmt, make_stack_exec);
break;
case LOAD_ACTION_START_TRACED:
traced = true;
/* Fall through. */
case LOAD_ACTION_START: {
word_t *cursor2 = (word_t *) stmt->start.stack_pointer;
const word_t argc = cursor2[0];
const word_t at_execfn = cursor2[1];
word_t name;
status = SYSCALL(CLOSE, 1, fd);
if (unlikely((int) status < 0))
FATAL();
/* Right after execve, the stack content is as follow:
*
* +------+--------+--------+--------+
* | argc | argv[] | envp[] | auxv[] |
* +------+--------+--------+--------+
*/
/* Skip argv[]. */
cursor2 += argc + 1;
/* Skip envp[]. */
do cursor2++; while (cursor2[0] != 0);
cursor2++;
/* Adjust auxv[]. */
do {
switch (cursor2[0]) {
case AT_PHDR:
cursor2[1] = stmt->start.at_phdr;
break;
case AT_PHENT:
cursor2[1] = stmt->start.at_phent;
break;
case AT_PHNUM:
cursor2[1] = stmt->start.at_phnum;
break;
case AT_ENTRY:
cursor2[1] = stmt->start.at_entry;
break;
case AT_BASE:
cursor2[1] = at_base;
break;
case AT_EXECFN:
/* stmt->start.at_execfn can't be used for now since it is
* currently stored in a location that will be scratched
* by the process (below the final stack pointer). */
cursor2[1] = at_execfn;
break;
default:
break;
}
cursor2 += 2;
} while (cursor2[0] != AT_NULL);
/* Note that only 2 arguments are actually necessary... */
name = basename(stmt->start.at_execfn);
SYSCALL(PRCTL, 3, PR_SET_NAME, name, 0);
if (unlikely(traced))
SYSCALL(EXECVE, 6, 1,
stmt->start.stack_pointer,
stmt->start.entry_point, 2, 3, 4);
else
BRANCH(stmt->start.stack_pointer, stmt->start.entry_point);
FATAL();
}
default:
FATAL();
}
}
FATAL();
}
@@ -0,0 +1,78 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SCRIPT
#define SCRIPT
#include "arch.h"
#include "attribute.h"
struct load_statement {
word_t action;
union {
struct {
word_t string_address;
} open;
struct {
word_t addr;
word_t length;
word_t prot;
word_t offset;
word_t clear_length;
} mmap;
struct {
word_t start;
} make_stack_exec;
struct {
word_t stack_pointer;
word_t entry_point;
word_t at_phdr;
word_t at_phent;
word_t at_phnum;
word_t at_entry;
word_t at_execfn;
} start;
};
} PACKED;
typedef struct load_statement LoadStatement;
#define LOAD_STATEMENT_SIZE(statement, type) \
(sizeof((statement).action) + sizeof((statement).type))
/* Don't use enum, since sizeof(enum) doesn't have to be equal to
* sizeof(word_t). Keep values in the same order as their respective
* actions appear in loader.c to get a change GCC produces a jump
* table. */
#define LOAD_ACTION_OPEN_NEXT 0
#define LOAD_ACTION_OPEN 1
#define LOAD_ACTION_MMAP_FILE 2
#define LOAD_ACTION_MMAP_ANON 3
#define LOAD_ACTION_MAKE_STACK_EXEC 4
#define LOAD_ACTION_START_TRACED 5
#define LOAD_ACTION_START 6
#endif /* SCRIPT */
+728
View File
@@ -0,0 +1,728 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/stat.h> /* lstat(2), */
#include <unistd.h> /* getcwd(2), lstat(2), */
#include <string.h> /* string(3), */
#include <strings.h> /* bzero(3), */
#include <assert.h> /* assert(3), */
#include <limits.h> /* PATH_MAX, */
#include <errno.h> /* E* */
#include <sys/queue.h> /* CIRCLEQ_*, */
#include <talloc.h> /* talloc_*, */
#include "path/binding.h"
#include "path/path.h"
#include "path/canon.h"
#include "cli/note.h"
#include "compat.h"
#define HEAD(tracee, side) \
(side == GUEST \
? (tracee)->fs->bindings.guest \
: (side == HOST \
? (tracee)->fs->bindings.host \
: (tracee)->fs->bindings.pending))
#define NEXT(binding, side) \
(side == GUEST \
? CIRCLEQ_NEXT(binding, link.guest) \
: (side == HOST \
? CIRCLEQ_NEXT(binding, link.host) \
: CIRCLEQ_NEXT(binding, link.pending)))
#define CIRCLEQ_FOREACH_(tracee, binding, side) \
for (binding = CIRCLEQ_FIRST(HEAD(tracee, side)); \
binding != (void *) HEAD(tracee, side); \
binding = NEXT(binding, side))
#define CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side) do { \
switch (side) { \
case GUEST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.guest); break; \
case HOST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.host); break; \
default: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.pending); break; \
} \
(void) talloc_reference(HEAD(tracee, side), binding); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side) do { \
switch (side) { \
case GUEST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.guest); break; \
case HOST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.host); break; \
default: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.pending); break; \
} \
(void) talloc_reference(HEAD(tracee, side), binding); \
} while (0)
#define CIRCLEQ_INSERT_HEAD_(tracee, binding, side) do { \
switch (side) { \
case GUEST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.guest); break; \
case HOST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.host); break; \
default: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.pending); break; \
} \
(void) talloc_reference(HEAD(tracee, side), binding); \
} while (0)
#define IS_LINKED(binding, link) \
((binding)->link.cqe_next != NULL && (binding)->link.cqe_prev != NULL)
#define CIRCLEQ_REMOVE_(tracee, binding, name) do { \
CIRCLEQ_REMOVE((tracee)->fs->bindings.name, binding, link.name);\
(binding)->link.name.cqe_next = NULL; \
(binding)->link.name.cqe_prev = NULL; \
talloc_unlink((tracee)->fs->bindings.name, binding); \
} while (0)
/**
* Print all bindings (verbose purpose).
*/
static void print_bindings(const Tracee *tracee)
{
const Binding *binding;
if (tracee->fs->bindings.guest == NULL)
return;
CIRCLEQ_FOREACH_(tracee, binding, GUEST) {
if (compare_paths(binding->host.path, binding->guest.path) == PATHS_ARE_EQUAL)
note(tracee, INFO, USER, "binding = %s", binding->host.path);
else
note(tracee, INFO, USER, "binding = %s:%s",
binding->host.path, binding->guest.path);
}
}
/**
* Get the binding for the given @path (relatively to the given
* binding @side).
*/
Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX])
{
Binding *binding;
size_t path_length = strlen(path);
/* Sanity checks. */
assert(path != NULL && path[0] == '/');
CIRCLEQ_FOREACH_(tracee, binding, side) {
Comparison comparison;
const Path *ref;
switch (side) {
case GUEST:
ref = &binding->guest;
break;
case HOST:
ref = &binding->host;
break;
default:
assert(0);
return NULL;
}
comparison = compare_paths2(ref->path, ref->length, path, path_length);
if ( comparison != PATHS_ARE_EQUAL
&& comparison != PATH1_IS_PREFIX)
continue;
/* Avoid false positive when a prefix of the rootfs is
* used as an asymmetric binding, ex.:
*
* proot -m /usr:/location /usr/local/slackware
*/
if ( side == HOST
&& compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL
&& belongs_to_guestfs(tracee, path))
continue;
return binding;
}
return NULL;
}
/**
* Get the binding path for the given @path (relatively to the given
* binding @side).
*/
const char *get_path_binding(const Tracee *tracee, Side side, const char path[PATH_MAX])
{
const Binding *binding;
binding = get_binding(tracee, side, path);
if (!binding)
return NULL;
switch (side) {
case GUEST:
return binding->guest.path;
case HOST:
return binding->host.path;
default:
assert(0);
return NULL;
}
}
/**
* Return the path to the guest rootfs for the given @tracee, from the
* host point-of-view obviously. Depending on whether
* initialize_bindings() was called or not, the path is retrieved from
* the "bindings.guest" list or from the "bindings.pending" list,
* respectively.
*/
const char *get_root(const Tracee* tracee)
{
const Binding *binding;
if (tracee == NULL || tracee->fs == NULL)
return NULL;
if (tracee->fs->bindings.guest == NULL) {
if (tracee->fs->bindings.pending == NULL
|| CIRCLEQ_EMPTY(tracee->fs->bindings.pending))
return NULL;
binding = CIRCLEQ_LAST(tracee->fs->bindings.pending);
if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL)
return NULL;
return binding->host.path;
}
assert(!CIRCLEQ_EMPTY(tracee->fs->bindings.guest));
binding = CIRCLEQ_LAST(tracee->fs->bindings.guest);
assert(strcmp(binding->guest.path, "/") == 0);
return binding->host.path;
}
/**
* Substitute the guest path (if any) with the host path in @path.
* This function returns:
*
* * -errno if an error occured
*
* * 0 if it is a binding location but no substitution is needed
* ("symetric" binding)
*
* * 1 if it is a binding location and a substitution was performed
* ("asymmetric" binding)
*/
int substitute_binding(const Tracee *tracee, Side side, char path[PATH_MAX])
{
const Path *reverse_ref;
const Path *ref;
const Binding *binding;
binding = get_binding(tracee, side, path);
if (!binding)
return -ENOENT;
/* Is it a "symetric" binding? */
if (!binding->need_substitution)
return 0;
switch (side) {
case GUEST:
ref = &binding->guest;
reverse_ref = &binding->host;
break;
case HOST:
ref = &binding->host;
reverse_ref = &binding->guest;
break;
default:
assert(0);
return -EACCES;
}
substitute_path_prefix(path, ref->length, reverse_ref->path, reverse_ref->length);
return 1;
}
/**
* Remove @binding from all the @tracee's lists of bindings it belongs to.
*/
void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding)
{
if (IS_LINKED(binding, link.pending))
CIRCLEQ_REMOVE_(tracee, binding, pending);
if (IS_LINKED(binding, link.guest))
CIRCLEQ_REMOVE_(tracee, binding, guest);
if (IS_LINKED(binding, link.host))
CIRCLEQ_REMOVE_(tracee, binding, host);
}
/**
* Insert @binding into the list of @bindings, in a sorted manner so
* as to make the substitution of nested bindings determistic, ex.:
*
* -b /bin:/foo/bin -b /usr/bin/more:/foo/bin/more
*
* Note: "nested" from the @side point-of-view.
*/
static void insort_binding(const Tracee *tracee, Side side, Binding *binding)
{
Binding *iterator;
Binding *previous = NULL;
Binding *next = CIRCLEQ_FIRST(HEAD(tracee, side));
/* Find where it should be added in the list. */
CIRCLEQ_FOREACH_(tracee, iterator, side) {
Comparison comparison;
const Path *binding_path;
const Path *iterator_path;
switch (side) {
case PENDING:
case GUEST:
binding_path = &binding->guest;
iterator_path = &iterator->guest;
break;
case HOST:
binding_path = &binding->host;
iterator_path = &iterator->host;
break;
default:
assert(0);
return;
}
comparison = compare_paths2(binding_path->path, binding_path->length,
iterator_path->path, iterator_path->length);
switch (comparison) {
case PATHS_ARE_EQUAL:
if (side == HOST) {
previous = iterator;
break;
}
if (tracee->verbose > 0 && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL) {
note(tracee, WARNING, USER,
"both '%s' and '%s' are bound to '%s', "
"only the last binding is active.",
iterator->host.path, binding->host.path,
binding->guest.path);
}
/* Replace this iterator with the new binding. */
CIRCLEQ_INSERT_AFTER_(tracee, iterator, binding, side);
remove_binding_from_all_lists(tracee, iterator);
return;
case PATH1_IS_PREFIX:
/* The new binding contains the iterator. */
previous = iterator;
break;
case PATH2_IS_PREFIX:
/* The iterator contains the new binding.
* Use the deepest container. */
if (next == (void *) HEAD(tracee, side))
next = iterator;
break;
case PATHS_ARE_NOT_COMPARABLE:
break;
default:
assert(0);
return;
}
}
/* Insert this binding in the list. */
if (previous != NULL)
CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side);
else if (next != (void *) HEAD(tracee, side))
CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side);
else
CIRCLEQ_INSERT_HEAD_(tracee, binding, side);
}
/**
* c.f. function above.
*/
static void insort_binding2(const Tracee *tracee, Binding *binding)
{
binding->need_substitution =
compare_paths(binding->host.path, binding->guest.path) != PATHS_ARE_EQUAL;
insort_binding(tracee, GUEST, binding);
insort_binding(tracee, HOST, binding);
}
/**
* Create and insert a new binding (@host_path:@guest_path) into the
* list of @tracee's bindings. The Talloc parent of this new binding
* is @context. This function returns NULL if an error occurred,
* otherwise a pointer to the newly created binding.
*/
Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context,
const char host_path[PATH_MAX],
const char guest_path[PATH_MAX])
{
Binding *binding;
binding = talloc_zero(context, Binding);
if (binding == NULL)
return NULL;
strcpy(binding->host.path, host_path);
strcpy(binding->guest.path, guest_path);
binding->host.length = strlen(binding->host.path);
binding->guest.length = strlen(binding->guest.path);
insort_binding2(tracee, binding);
return binding;
}
/**
* Free all bindings from @bindings.
*
* Note: this is a Talloc destructor.
*/
static int remove_bindings(Bindings *bindings)
{
Binding *binding;
Tracee *tracee;
/* Unlink all bindings from the @link list. */
#define CIRCLEQ_REMOVE_ALL(name) do { \
binding = CIRCLEQ_FIRST(bindings); \
while (binding != (void *) bindings) { \
Binding *next = CIRCLEQ_NEXT(binding, link.name);\
CIRCLEQ_REMOVE_(tracee, binding, name); \
binding = next; \
} \
} while (0)
/* Search which link is used by this list. */
tracee = TRACEE(bindings);
if (bindings == tracee->fs->bindings.pending)
CIRCLEQ_REMOVE_ALL(pending);
else if (bindings == tracee->fs->bindings.guest)
CIRCLEQ_REMOVE_ALL(guest);
else if (bindings == tracee->fs->bindings.host)
CIRCLEQ_REMOVE_ALL(host);
bzero(bindings, sizeof(Bindings));
return 0;
}
/**
* Allocate a new binding "@host:@guest" and attach it to
* @tracee->fs->bindings.pending. This function complains about
* missing @host path only if @must_exist is true. This function
* returns the allocated binding on success, NULL on error.
*/
Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist)
{
Binding *binding;
char base[PATH_MAX];
int status;
/* Lasy allocation of the list of bindings specified by the
* user. This list will be used by initialize_bindings(). */
if (tracee->fs->bindings.pending == NULL) {
tracee->fs->bindings.pending = talloc_zero(tracee->fs, Bindings);
if (tracee->fs->bindings.pending == NULL)
return NULL;
CIRCLEQ_INIT(tracee->fs->bindings.pending);
talloc_set_destructor(tracee->fs->bindings.pending, remove_bindings);
}
/* Allocate an empty binding. */
binding = talloc_zero(tracee->ctx, Binding);
if (binding == NULL)
return NULL;
/* Canonicalize the host part of the binding, as expected by
* get_binding(). */
status = realpath2(tracee->reconf.tracee, binding->host.path, host, true);
if (status < 0) {
if (must_exist && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL)
note(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s",
host, strerror(-status));
goto error;
}
binding->host.length = strlen(binding->host.path);
/* Symetric binding? */
guest = guest ?: host;
/* When not absolute, assume the guest path is relative to the
* current working directory, as with ``-b .`` for instance. */
if (guest[0] != '/') {
status = getcwd2(tracee->reconf.tracee, base);
if (status < 0) {
note(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s",
binding->guest.path, strerror(-status));
goto error;
}
}
else
strcpy(base, "/");
join_paths(binding->guest.path, base, guest);
binding->guest.length = strlen(binding->guest.path);
/* Keep the list of bindings specified by the user ordered,
* for the sake of consistency. For instance binding to "/"
* has to be the last in the list. */
insort_binding(tracee, PENDING, binding);
return binding;
error:
TALLOC_FREE(binding);
return NULL;
}
/**
* Canonicalize the guest part of the given @binding, insert it into
* @tracee->fs->bindings.guest and @tracee->fs->bindings.host. This
* function returns -1 if an error occured, 0 otherwise.
*/
static void initialize_binding(Tracee *tracee, Binding *binding)
{
char path[PATH_MAX];
struct stat statl;
int status;
/* All bindings but "/" must be canonicalized. The exception
* for "/" is required to bootstrap the canonicalization. */
if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL) {
bool dereference;
size_t length;
strcpy(path, binding->guest.path);
length = strlen(path);
assert(length > 0);
/* Does the user explicitly tell not to dereference
* guest path? */
dereference = (path[length - 1] != '!');
if (!dereference)
path[length - 1] = '\0';
/* Initial state before canonicalization. */
strcpy(binding->guest.path, "/");
/* Remember the type of the final component, it will
* be used in build_glue() later. */
status = lstat(binding->host.path, &statl);
tracee->glue_type = (status < 0 || S_ISBLK(statl.st_mode) || S_ISCHR(statl.st_mode)
? S_IFREG : statl.st_mode & S_IFMT);
/* Sanitize the guest path of the binding within the
alternate rootfs since it is assumed by
substitute_binding(). */
status = canonicalize(tracee, path, dereference, binding->guest.path, 0);
if (status < 0) {
note(tracee, WARNING, INTERNAL,
"sanitizing the guest path (binding) \"%s\": %s",
path, strerror(-status));
return;
}
/* Remove the trailing "/" or "/." as expected by
* substitute_binding(). */
chop_finality(binding->guest.path);
/* Disable definitively the creation of the glue for
* this binding. */
tracee->glue_type = 0;
}
binding->guest.length = strlen(binding->guest.path);
insort_binding2(tracee, binding);
}
/**
* Add bindings induced by @new_binding when @tracee is being sub-reconfigured.
* For example, if the previous configuration ("-r /rootfs1") contains this
* binding:
*
* -b /home/ced:/usr/local/ced
*
* and if the current configuration ("-r /rootfs2") introduces such a new
* binding:
*
* -b /usr:/media
*
* then the following binding is induced:
*
* -b /home/ced:/media/local/ced
*/
static void add_induced_bindings(Tracee *tracee, const Binding *new_binding)
{
Binding *old_binding;
char path[PATH_MAX];
int status;
/* Only for reconfiguration. */
if (tracee->reconf.tracee == NULL)
return;
/* From the example, PRoot has already converted "-b /usr:/media" into
* "-b /rootfs1/usr:/media" in order to ensure the host part is really a
* host path. Here, the host part is converted back to "/usr" since the
* comparison can't be made on "/rootfs1/usr".
*/
strcpy(path, new_binding->host.path);
status = detranslate_path(tracee->reconf.tracee, path, NULL);
if (status < 0)
return;
CIRCLEQ_FOREACH_(tracee->reconf.tracee, old_binding, GUEST) {
Binding *induced_binding;
Comparison comparison;
char path2[PATH_MAX];
size_t prefix_length;
/* Check if there's an induced binding by searching a common
* path prefix in between new/old bindings:
*
* -b /home/ced:[/usr]/local/ced
* -b [/usr]:/media
*/
comparison = compare_paths(path, old_binding->guest.path);
if (comparison != PATH1_IS_PREFIX)
continue;
/* Convert the path of this induced binding to the new
* filesystem namespace. From the example, "/usr/local/ced" is
* converted into "/media/local/ced". Note: substitute_binding
* can't be used in this case since it would expect
* "/rootfs1/usr/local/ced instead".
*/
prefix_length = strlen(path);
if (prefix_length == 1)
prefix_length = 0;
join_paths(path2, new_binding->guest.path, old_binding->guest.path + prefix_length);
/* Install the induced binding. From the example:
*
* -b /home/ced:/media/local/ced
*/
induced_binding = talloc_zero(tracee->ctx, Binding);
if (induced_binding == NULL)
continue;
strcpy(induced_binding->host.path, old_binding->host.path);
strcpy(induced_binding->guest.path, path2);
induced_binding->host.length = strlen(induced_binding->host.path);
induced_binding->guest.length = strlen(induced_binding->guest.path);
VERBOSE(tracee, 2, "induced binding: %s:%s (old) & %s:%s (new) -> %s:%s (induced)",
old_binding->host.path, old_binding->guest.path, path, new_binding->guest.path,
induced_binding->host.path, induced_binding->guest.path);
insort_binding2(tracee, induced_binding);
}
}
/**
* Allocate @tracee->fs->bindings.guest and
* @tracee->fs->bindings.host, then call initialize_binding() on each
* binding listed in @tracee->fs->bindings.pending.
*/
int initialize_bindings(Tracee *tracee)
{
Binding *binding;
/* Sanity checks. */
assert(get_root(tracee) != NULL);
assert(tracee->fs->bindings.pending != NULL);
assert(tracee->fs->bindings.guest == NULL);
assert(tracee->fs->bindings.host == NULL);
/* Allocate @tracee->fs->bindings.guest and
* @tracee->fs->bindings.host. */
tracee->fs->bindings.guest = talloc_zero(tracee->fs, Bindings);
tracee->fs->bindings.host = talloc_zero(tracee->fs, Bindings);
if (tracee->fs->bindings.guest == NULL || tracee->fs->bindings.host == NULL) {
note(tracee, ERROR, INTERNAL, "can't allocate enough memory");
TALLOC_FREE(tracee->fs->bindings.guest);
TALLOC_FREE(tracee->fs->bindings.host);
return -1;
}
CIRCLEQ_INIT(tracee->fs->bindings.guest);
CIRCLEQ_INIT(tracee->fs->bindings.host);
talloc_set_destructor(tracee->fs->bindings.guest, remove_bindings);
talloc_set_destructor(tracee->fs->bindings.host, remove_bindings);
/* The binding to "/" has to be installed before other
* bindings since this former is required to canonicalize
* these latters. */
binding = CIRCLEQ_LAST(tracee->fs->bindings.pending);
assert(compare_paths(binding->guest.path, "/") == PATHS_ARE_EQUAL);
/* Call initialize_binding() on each pending binding in
* reverse order: the last binding "/" is used to bootstrap
* the canonicalization. */
while (binding != (void *) tracee->fs->bindings.pending) {
Binding *previous;
previous = CIRCLEQ_PREV(binding, link.pending);
/* Canonicalize then insert this binding into
* tracee->fs->bindings.guest/host. */
initialize_binding(tracee, binding);
/* Add induced bindings on sub-reconfiguration. */
add_induced_bindings(tracee, binding);
binding = previous;
}
TALLOC_FREE(tracee->fs->bindings.pending);
if (tracee->verbose > 0)
print_bindings(tracee);
return 0;
}
+57
View File
@@ -0,0 +1,57 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef BINDING_H
#define BINDING_H
#include <limits.h> /* PATH_MAX, */
#include <stdbool.h>
#include "tracee/tracee.h"
#include "path.h"
typedef struct binding {
Path host;
Path guest;
bool need_substitution;
struct {
CIRCLEQ_ENTRY(binding) pending;
CIRCLEQ_ENTRY(binding) guest;
CIRCLEQ_ENTRY(binding) host;
} link;
} Binding;
typedef CIRCLEQ_HEAD(bindings, binding) Bindings;
extern Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context,
const char host_path[PATH_MAX], const char guest_path[PATH_MAX]);
extern Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist);
extern int initialize_bindings(Tracee *tracee);
extern const char *get_path_binding(const Tracee* tracee, Side side, const char path[PATH_MAX]);
extern Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]);
extern const char *get_root(const Tracee* tracee);
extern int substitute_binding(const Tracee* tracee, Side side, char path[PATH_MAX]);
extern void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding);
#endif /* BINDING_H */
+340
View File
@@ -0,0 +1,340 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/types.h> /* pid_t */
#include <limits.h> /* PATH_MAX, */
#include <sys/param.h> /* MAXSYMLINKS, */
#include <errno.h> /* E*, */
#include <sys/stat.h> /* lstat(2), S_ISREG(), */
#include <unistd.h> /* access(2), lstat(2), */
#include <string.h> /* string(3), */
#include <assert.h> /* assert(3), */
#include <stdio.h> /* sscanf(3), */
#include "path/canon.h"
#include "path/path.h"
#include "path/binding.h"
#include "path/glue.h"
#include "path/proc.h"
/**
* Put an end-of-string ('\0') right before the last component of @path.
*/
static inline void pop_component(char *path)
{
int offset;
offset = strlen(path) - 1;
assert(offset >= 0);
/* Don't pop over "/", it doesn't mean anything. */
if (offset == 0) {
assert(path[0] == '/' && path[1] == '\0');
return;
}
/* Skip trailing path separators. */
while (offset > 1 && path[offset] == '/')
offset--;
/* Search for the previous path separator. */
while (offset > 1 && path[offset] != '/')
offset--;
/* Cut the end of the string before the last component. */
path[offset] = '\0';
assert(path[0] == '/');
}
/**
* Copy in @component the first path component pointed to by @cursor,
* this later is updated to point to the next component for a further
* call. This function returns:
*
* - -errno if an error occured.
*
* - FINAL_SLASH if it the last component of the path but we
* really expect a directory.
*
* - FINAL_NORMAL if it the last component of the path.
*
* - 0 otherwise.
*/
static inline Finality next_component(char component[NAME_MAX], const char **cursor)
{
const char *start;
ptrdiff_t length;
bool want_dir;
/* Skip leading path separators. */
while (**cursor != '\0' && **cursor == '/')
(*cursor)++;
/* Find the next component. */
start = *cursor;
while (**cursor != '\0' && **cursor != '/')
(*cursor)++;
length = *cursor - start;
if (length >= NAME_MAX)
return -ENAMETOOLONG;
/* Extract the component. */
strncpy(component, start, length);
component[length] = '\0';
/* Check if a [link to a] directory is expected. */
want_dir = (**cursor == '/');
/* Skip trailing path separators. */
while (**cursor != '\0' && **cursor == '/')
(*cursor)++;
if (**cursor == '\0')
return (want_dir
? FINAL_SLASH
: FINAL_NORMAL);
return NOT_FINAL;
}
/**
* Resolve bindings (if any) in @guest_path and copy the translated
* path into @host_path. Also, this function checks that a non-final
* component is either a directory (returned value is 0) or a symlink
* (returned value is 1), otherwise it returns -errno (-ENOENT or
* -ENOTDIR).
*/
static inline int substitute_binding_stat(Tracee *tracee, Finality finality, unsigned int recursion_level,
const char guest_path[PATH_MAX], char host_path[PATH_MAX])
{
struct stat statl;
int status;
strcpy(host_path, guest_path);
status = substitute_binding(tracee, GUEST, host_path);
if (status < 0)
return status;
statl.st_mode = 0;
status = lstat(host_path, &statl);
/* Build the glue between the hostfs and the guestfs during
* the initialization of a binding. */
if (status < 0 && tracee->glue_type != 0) {
statl.st_mode = build_glue(tracee, guest_path, host_path, finality);
if (statl.st_mode == 0)
status = -1;
}
/* Return an error if a non-final component isn't a directory
* nor a symlink. The error depends on why the component
* could not be accessed (ENOENT, EACCES, ...), otherwise the
* error is "Not a directory". */
if (!IS_FINAL(finality) && !S_ISDIR(statl.st_mode) && !S_ISLNK(statl.st_mode))
return (status < 0 ? -ENOENT : -ENOTDIR);
return (S_ISLNK(statl.st_mode) ? 1 : 0);
}
/**
* Copy in @guest_path the canonicalization (see `man 3 realpath`) of
* @user_path regarding to @tracee->root. The path to canonicalize
* could be either absolute or relative to @guest_path. When the last
* component of @user_path is a link, it is dereferenced only if
* @deref_final is true -- it is useful for syscalls like lstat(2).
* The parameter @recursion_level should be set to 0 unless you know
* what you are doing. This function returns -errno if an error
* occured, otherwise it returns 0.
*/
int canonicalize(Tracee *tracee, const char *user_path, bool deref_final,
char guest_path[PATH_MAX], unsigned int recursion_level)
{
char scratch_path[PATH_MAX];
char host_path[PATH_MAX];
Finality finality;
const char *cursor;
int status;
/* Avoid infinite loop on circular links. */
if (recursion_level > MAXSYMLINKS)
return -ELOOP;
if (user_path[0] != '/') {
/* Ensure 'guest_path' contains an absolute base of
* the relative `user_path`. */
if (guest_path[0] != '/')
return -EINVAL;
}
else
strcpy(guest_path, "/");
/* Resolve bindings for the initial '/' component or user_path,
* which is not handled in the loop below.
* In particular HOST_PATH extensions are called from there. */
status = substitute_binding_stat(tracee, NOT_FINAL, recursion_level, guest_path, host_path);
if (status < 0)
return status;
/* Canonicalize recursely 'user_path' into 'guest_path'. */
cursor = user_path;
finality = NOT_FINAL;
while (!IS_FINAL(finality)) {
Comparison comparison;
char component[NAME_MAX];
finality = next_component(component, &cursor);
status = (int) finality;
if (status < 0)
return status;
if (strcmp(component, ".") == 0) {
if (IS_FINAL(finality))
finality = FINAL_DOT;
continue;
}
if (strcmp(component, "..") == 0) {
pop_component(guest_path);
if (IS_FINAL(finality))
finality = FINAL_SLASH;
continue;
}
join_paths(scratch_path, guest_path, component);
/* Resolve bindings and check that a non-final
* component exists and either is a directory or is a
* symlink. For this latter case, we check that the
* symlink points to a directory once it is
* canonicalized, at the end of this loop. */
status = substitute_binding_stat(tracee, finality, recursion_level, scratch_path, host_path);
if (status < 0)
return status;
/* Nothing special to do if it's not a link or if we
* explicitly ask to not dereference 'user_path', as
* required by syscalls like lstat(2). Obviously, this
* later condition does not apply to intermediate path
* components. Errors are explicitly ignored since
* they should be handled by the caller. */
if (status <= 0 || (finality == FINAL_NORMAL && !deref_final)) {
strcpy(scratch_path, guest_path);
join_paths(guest_path, scratch_path, component);
continue;
}
/* It's a link, so we have to dereference *and*
* canonicalize to ensure we are not going outside the
* new root. */
comparison = compare_paths("/proc", guest_path);
switch (comparison) {
case PATHS_ARE_EQUAL:
case PATH1_IS_PREFIX:
/* Some links in "/proc" are generated
* dynamically by the kernel. PRoot has to
* emulate some of them. */
status = readlink_proc(tracee, scratch_path,
guest_path, component, comparison);
switch (status) {
case CANONICALIZE:
/* The symlink is already dereferenced,
* now canonicalize it. */
goto canon;
case DONT_CANONICALIZE:
/* If and only very final, this symlink
* shouldn't be dereferenced nor canonicalized. */
if (finality == FINAL_NORMAL) {
strcpy(guest_path, scratch_path);
return 0;
}
break;
default:
if (status < 0)
return status;
}
default:
break;
}
status = readlink(host_path, scratch_path, sizeof(scratch_path));
if (status < 0)
return status;
else if (status == sizeof(scratch_path))
return -ENAMETOOLONG;
scratch_path[status] = '\0';
/* Remove the leading "root" part if needed, it's
* useful for "/proc/self/cwd/" for instance. */
status = detranslate_path(tracee, scratch_path, host_path);
if (status < 0)
return status;
canon:
/* Canonicalize recursively the referee in case it
* is/contains a link, moreover if it is not an
* absolute link then it is relative to
* 'guest_path'. */
status = canonicalize(tracee, scratch_path, true, guest_path, recursion_level + 1);
if (status < 0)
return status;
/* Check that a non-final canonicalized/dereferenced
* symlink exists and is a directory. */
status = substitute_binding_stat(tracee, finality, recursion_level, guest_path, host_path);
if (status < 0)
return status;
/* Here, 'guest_path' shouldn't be a symlink anymore,
* unless it is a named file descriptor. */
assert(status != 1 || sscanf(guest_path, "/proc/%*d/fd/%d", &status) == 1);
}
/* At the exit stage of the first level of recursion,
* `guest_path` is fully canonicalized but a terminating '/'
* or a terminating '.' may be required to keep the initial
* semantic of `user_path`. */
if (recursion_level == 0) {
switch (finality) {
case FINAL_NORMAL:
break;
case FINAL_SLASH:
strcpy(scratch_path, guest_path);
join_paths(guest_path, scratch_path, "");
break;
case FINAL_DOT:
strcpy(scratch_path, guest_path);
join_paths(guest_path, scratch_path, ".");
break;
default:
assert(0);
}
}
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef CANON_H
#define CANON_H
#include <stdbool.h>
#include <limits.h>
#include "tracee/tracee.h"
extern int canonicalize(Tracee *tracee, const char *user_path, bool deref_final,
char guest_path[PATH_MAX], unsigned int nb_recursion);
#endif /* CANON_H */
+189
View File
@@ -0,0 +1,189 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/types.h> /* mkdir(2), lstat(2), */
#include <sys/stat.h> /* mkdir(2), lstat(2), */
#include <fcntl.h> /* mknod(2), */
#include <unistd.h> /* mknod(2), lstat(2), unlink(2), rmdir(2), */
#include <string.h> /* string(3), */
#include <assert.h> /* assert(3), */
#include <limits.h> /* PATH_MAX, */
#include <errno.h> /* errno, E* */
#include <talloc.h> /* talloc_*, */
#include "path/binding.h"
#include "path/path.h"
#include "path/temp.h"
#include "cli/note.h"
#include "compat.h"
/**
* Remove @path if it is empty only.
*
* Note: this is a Talloc destructor.
*/
static int remove_placeholder(char *path)
{
struct stat statl;
int status;
status = lstat(path, &statl);
if (status)
return 0; /* Not fatal. */
if (!S_ISDIR(statl.st_mode)) {
if (statl.st_size != 0)
return 0; /* Not fatal. */
status = unlink(path);
}
else
status = rmdir(path);
if (status)
return 0; /* Not fatal. */
return 0;
}
/**
* Attach a copy of @path to the autofree context, and set its
* destructor to remove_placeholder().
*/
static void set_placeholder_destructor(const char *path)
{
TALLOC_CTX *autofreed;
char *placeholder;
autofreed = talloc_autofree_context();
if (autofreed == NULL)
return;
placeholder = talloc_strdup(autofreed, path);
if (placeholder == NULL)
return;
talloc_set_destructor(placeholder, remove_placeholder);
}
/**
* Build in a temporary filesystem the glue between the guest part and
* the host part of the @binding_path. This function returns the type
* of the bound path, otherwise 0 if an error occured.
*
* For example, assuming the host path "/opt" is mounted/bound to the
* guest path "/black/holes/and/revelations", and assuming this path
* can't be created in the guest rootfs (eg. permission denied), then
* it is created in a temporary rootfs and all these paths are glued
* that way:
*
* $GUEST/black/ --> $GLUE/black/
* ./holes
* ./holes/and
* ./holes/and/revelations --> $HOST/opt/
*
* This glue allows operations on paths that do not exist in the guest
* rootfs but that were specified as the guest part of a binding.
*/
mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX],
Finality finality)
{
bool belongs_to_gluefs;
Comparison comparison;
Binding *binding;
mode_t type;
mode_t mode;
int status;
assert(tracee->glue_type != 0);
/* Create the temporary directory where the "glue" rootfs will
* lie. */
if (tracee->glue == NULL) {
tracee->glue = create_temp_directory(NULL, global_tool_name);
if (tracee->glue == NULL) {
note(tracee, ERROR, INTERNAL, "can't create glue rootfs");
return 0;
}
talloc_set_name_const(tracee->glue, "$glue");
}
comparison = compare_paths(tracee->glue, host_path);
belongs_to_gluefs = (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX);
/* If it's not a final component then it is a directory. I definitely
* hate how the potential type of the final component is propagated
* from initialize_binding() down to here, sadly there's no elegant way
* to know its type at this stage. */
if (IS_FINAL(finality)) {
type = tracee->glue_type;
mode = (belongs_to_gluefs ? 0777 : 0);
}
else {
type = S_IFDIR;
mode = 0777;
}
if (getenv("PROOT_DONT_POLLUTE_ROOTFS") != NULL && !belongs_to_gluefs)
goto create_binding;
/* Try to create this component into the "guest" or "glue"
* rootfs (depending if there were a glue previously). */
if (S_ISDIR(type))
status = mkdir(host_path, mode);
else /* S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO or S_IFSOCK. */
status = mknod(host_path, mode | type, 0);
/* Remove placeholders from the guest rootfs once PRoot is
* terminated. */
if (status >= 0 && !belongs_to_gluefs)
set_placeholder_destructor(host_path);
/* Nothing else to do if the path already exists or if it is
* the final component since it will be pointed to by the
* binding being initialized (from the example,
* "$GUEST/black/holes/and/revelations" -> "$HOST/opt"). */
if (status >= 0 || errno == EEXIST || IS_FINAL(finality))
return type;
/* mkdir/mknod are supposed to always succeed in
* tracee->glue. */
if (belongs_to_gluefs) {
note(tracee, WARNING, SYSTEM, "mkdir/mknod");
return 0;
}
create_binding:
/* Sanity checks. */
if ( strnlen(tracee->glue, PATH_MAX) >= PATH_MAX
|| strnlen(guest_path, PATH_MAX) >= PATH_MAX) {
note(tracee, WARNING, INTERNAL, "installing the binding: guest path too long");
return 0;
}
/* From the example, create the binding "/black" ->
* "$GLUE/black". */
binding = insort_binding3(tracee, tracee->glue, tracee->glue, guest_path);
if (binding == NULL)
return 0;
return type;
}
+34
View File
@@ -0,0 +1,34 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef GLUE_H
#define GLUE_H
#include <limits.h> /* PATH_MAX, */
#include "tracee/tracee.h"
#include "path.h"
extern mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX],
Finality finality);
#endif /* GLUE_H */
+661
View File
@@ -0,0 +1,661 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <string.h> /* string(3), */
#include <stdarg.h> /* va_*(3), */
#include <assert.h> /* assert(3), */
#include <fcntl.h> /* AT_*, */
#include <unistd.h> /* readlink*(2), *stat(2), getpid(2), */
#include <sys/types.h> /* pid_t, */
#include <sys/stat.h> /* S_ISDIR, */
#include <dirent.h> /* opendir(3), readdir(3), */
#include <stdio.h> /* snprintf(3), */
#include <errno.h> /* E*, */
#include <stddef.h> /* ptrdiff_t, */
#include <inttypes.h> /* PRI*, */
#include "path/path.h"
#include "path/binding.h"
#include "path/canon.h"
#include "path/proc.h"
#include "cli/note.h"
#include "compat.h"
/**
* Copy in @result the concatenation of several paths (@number_paths)
* and adds a path separator ('/') in between when needed. This
* function returns -errno if an error occured, otherwise it returns 0.
*/
void join_paths(char result[PATH_MAX], const char *path1, const char *path2)
{
size_t length;
strcpy(result, path1);
length = strlen(result);
/* A new path separator is needed. */
if (length > 0 && result[length - 1] != '/' && path2[0] != '/') {
strcat(result + length, "/");
strcat(result + length, path2);
}
/* There are already two path separators. */
else if (length > 0 && result[length - 1] == '/' && path2[0] == '/') {
strcat(result + length, path2 + 1);
}
/* There's already one path separator or result[] is empty. */
else
strcat(result + length, path2);
}
/**
* Put in @host_path the full path to the given shell @command. The
* @command is searched in @paths if not null, otherwise in $PATH
* (relatively to the @tracee's file-system name-space). This
* function always returns -1 on error, otherwise 0.
*/
int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], const char *command)
{
char path[PATH_MAX];
const char *cursor;
struct stat statr;
int status;
bool is_explicit;
bool found;
assert(command != NULL);
is_explicit = (strchr(command, '/') != NULL);
/* Is the command available without any $PATH look-up? */
status = realpath2(tracee, host_path, command, true);
if (status == 0 && stat(host_path, &statr) == 0) {
if (is_explicit && !S_ISREG(statr.st_mode)) {
note(tracee, ERROR, USER, "'%s' is not a regular file", command);
return -EACCES;
}
if (is_explicit && (statr.st_mode & S_IXUSR) == 0) {
note(tracee, ERROR, USER, "'%s' is not executable", command);
return -EACCES;
}
found = true;
/* Don't dereference the final component to preserve
* argv0 in case it is a symlink to script. */
(void) realpath2(tracee, host_path, command, false);
}
else
found = false;
/* Is the the explicit command was found? */
if (is_explicit) {
if (found)
return 0;
else
goto not_found;
}
/* Otherwise search the command in $PATH. */
paths = paths ?: getenv("PATH");
if (paths == NULL || strcmp(paths, "") == 0)
goto not_found;
cursor = paths;
do {
size_t length;
length = strcspn(cursor, ":");
cursor += length + 1;
if (length >= PATH_MAX)
continue;
else if (length == 0)
strcpy(path, ".");
else {
strncpy(path, cursor - length - 1, length);
path[length] = '\0';
}
/* Avoid buffer-overflow. */
if (length + strlen(command) + 2 >= PATH_MAX)
continue;
strcat(path, "/");
strcat(path, command);
status = realpath2(tracee, host_path, path, true);
if (status == 0
&& stat(host_path, &statr) == 0
&& S_ISREG(statr.st_mode)
&& (statr.st_mode & S_IXUSR) != 0) {
/* Don't dereference the final component to preserve
* argv0 in case it is a symlink to script. */
(void) realpath2(tracee, host_path, path, false);
return 0;
}
} while (*(cursor - 1) != '\0');
not_found:
status = getcwd2(tracee, path);
if (status < 0)
strcpy(path, "<unknown>");
note(tracee, ERROR, USER, "'%s' not found (root = %s, cwd = %s, $PATH=%s)",
command, get_root(tracee), path, paths);
/* Check if the command was found without any $PATH look-up
* but it didn't contain "/". */
if (found && !is_explicit)
note(tracee, ERROR, USER,
"to execute a local program, use the './' prefix, for example: ./%s", command);
return -1;
}
/**
* Put in @host_path the canonicalized form of @path. In the nominal
* case (@tracee == NULL), this function is barely equivalent to
* realpath(), but when doing sub-reconfiguration, the path is
* canonicalized relatively to the current @tracee's file-system
* name-space. This function returns -errno on error, otherwise 0.
*/
int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final)
{
int status;
if (tracee == NULL)
status = (realpath(path, host_path) == NULL ? -errno : 0);
else
status = translate_path(tracee, host_path, AT_FDCWD, path, deref_final);
return status;
}
/**
* Put in @guest_path the canonicalized current working directory. In
* the nominal case (@tracee == NULL), this function is barely
* equivalent to realpath(), but when doing sub-reconfiguration, the
* path is canonicalized relatively to the current @tracee's
* file-system name-space. This function returns -errno on error,
* otherwise 0.
*/
int getcwd2(Tracee *tracee, char guest_path[PATH_MAX])
{
if (tracee == NULL) {
if (getcwd(guest_path, PATH_MAX) == NULL)
return -errno;
}
else
strcpy(guest_path, tracee->fs->cwd);
return 0;
}
/**
* Remove the trailing "/" or "/.".
*/
void chop_finality(char *path)
{
size_t length = strlen(path);
if (path[length - 1] == '.') {
assert(length >= 2);
/* Special case for "/." */
if (length == 2)
path[length - 1] = '\0';
else
path[length - 2] = '\0';
}
else if (path[length - 1] == '/') {
/* Special case for "/" */
if (length > 1)
path[length - 1] = '\0';
}
}
/**
* Put in @path the result of readlink(/proc/@pid/fd/@fd). This
* function returns -errno if an error occured, otherwise 0.
*/
int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX])
{
char link[32]; /* 32 > sizeof("/proc//cwd") + sizeof(#ULONG_MAX) */
int status;
/* Format the path to the "virtual" link. */
status = snprintf(link, sizeof(link), "/proc/%d/fd/%d", pid, fd);
if (status < 0)
return -EBADF;
if ((size_t) status >= sizeof(link))
return -EBADF;
/* Read the value of this "virtual" link. */
status = readlink(link, path, PATH_MAX);
if (status < 0)
return -EBADF;
if (status >= PATH_MAX)
return -ENAMETOOLONG;
path[status] = '\0';
return 0;
}
/**
* Copy in @result the equivalent of "@tracee->root + canon(@dir_fd +
* @user_path)". If @user_path is not absolute then it is relative to
* the directory referred by the descriptor @dir_fd (AT_FDCWD is for
* the current working directory). See the documentation of
* canonicalize() for the meaning of @deref_final. This function
* returns -errno if an error occured, otherwise 0.
*/
int translate_path(Tracee *tracee, char result[PATH_MAX], int dir_fd,
const char *user_path, bool deref_final)
{
char guest_path[PATH_MAX];
int status;
/* Use "/" as the base if it is an absolute guest path. */
if (user_path[0] == '/') {
strcpy(result, "/");
}
/* It is relative to a directory referred by a descriptor, see
* openat(2) for details. */
else if (dir_fd != AT_FDCWD) {
/* /proc/@tracee->pid/fd/@dir_fd -> result. */
status = readlink_proc_pid_fd(tracee->pid, dir_fd, result);
if (status < 0)
return status;
/* Named file descriptors may reference special
* objects like pipes, sockets, inodes, ... Such
* objects do not belong to the file-system. */
if (result[0] != '/')
return -ENOTDIR;
/* Remove the leading "root" part of the base
* (required!). */
status = detranslate_path(tracee, result, NULL);
if (status < 0)
return status;
}
/* It is relative to the current working directory. */
else {
status = getcwd2(tracee, result);
if (status < 0)
return status;
}
VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")",
tracee != NULL ? tracee->pid : 0, result, user_path);
/* So far "result" was used as a base path, it's time to join
* it to the user path. */
join_paths(guest_path, result, user_path);
strcpy(result, "/");
/* Canonicalize regarding the new root. */
status = canonicalize(tracee, guest_path, deref_final, result, 0);
if (status < 0)
return status;
/* Final binding substitution to convert "result" into a host
* path, since canonicalize() works from the guest
* point-of-view. */
status = substitute_binding(tracee, GUEST, result);
if (status < 0)
return status;
return 0;
}
/**
* Remove/substitute the leading part of a "translated" @path. It
* returns 0 if no transformation is required (ie. symmetric binding),
* otherwise it returns the size in bytes of the updated @path,
* including the end-of-string terminator. On error it returns
* -errno.
*/
int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX])
{
size_t prefix_length;
ssize_t new_length;
bool sanity_check;
bool follow_binding;
/* Don't try to detranslate relative paths (typically the
* target of a relative symbolic link). */
if (path[0] != '/')
return 0;
/* Is it a symlink? */
if (t_referrer != NULL) {
Comparison comparison;
sanity_check = false;
follow_binding = false;
/* In some cases bindings have to be resolved. */
comparison = compare_paths("/proc", t_referrer);
if (comparison == PATH1_IS_PREFIX) {
/* Some links in "/proc" are generated
* dynamically by the kernel. PRoot has to
* emulate some of them. */
char proc_path[PATH_MAX];
strcpy(proc_path, path);
new_length = readlink_proc2(tracee, proc_path, t_referrer);
if (new_length < 0)
return new_length;
if (new_length != 0) {
strcpy(path, proc_path);
return new_length + 1;
}
/* Always resolve bindings for symlinks in
* "/proc", they always point to the emulated
* file-system namespace by design. */
follow_binding = true;
}
else if (!belongs_to_guestfs(tracee, t_referrer)) {
const char *binding_referree;
const char *binding_referrer;
binding_referree = get_path_binding(tracee, HOST, path);
binding_referrer = get_path_binding(tracee, HOST, t_referrer);
assert(binding_referrer != NULL);
/* Resolve bindings for symlinks that belong
* to a binding and point to the same binding.
* For example, if "-b /lib:/foo" is specified
* and the symlink "/lib/a -> /lib/b" exists
* in the host rootfs namespace, then it
* should appear as "/foo/a -> /foo/b" in the
* guest rootfs namespace for consistency
* reasons. */
if (binding_referree != NULL) {
comparison = compare_paths(binding_referree, binding_referrer);
follow_binding = (comparison == PATHS_ARE_EQUAL);
}
}
}
else {
sanity_check = true;
follow_binding = true;
}
if (follow_binding) {
switch (substitute_binding(tracee, HOST, path)) {
case 0:
return 0;
case 1:
return strlen(path) + 1;
default:
break;
}
}
switch (compare_paths(get_root(tracee), path)) {
case PATH1_IS_PREFIX:
/* Remove the leading part, that is, the "root". */
prefix_length = strlen(get_root(tracee));
/* Special case when path to the guest rootfs == "/". */
if (prefix_length == 1)
prefix_length = 0;
new_length = strlen(path) - prefix_length;
memmove(path, path + prefix_length, new_length);
path[new_length] = '\0';
break;
case PATHS_ARE_EQUAL:
/* Special case when path == root. */
new_length = 1;
strcpy(path, "/");
break;
default:
/* Ensure the path is within the new root. */
if (sanity_check)
return -EPERM;
else
return 0;
}
return new_length + 1;
}
/**
* Check if the translated @host_path belongs to the guest rootfs,
* that is, isn't from a binding.
*/
bool belongs_to_guestfs(const Tracee *tracee, const char *host_path)
{
Comparison comparison;
comparison = compare_paths(get_root(tracee), host_path);
return (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX);
}
/**
* Compare @path1 with @path2, which are respectively @length1 and
* @length2 long.
*
* This function works only with paths canonicalized in the same
* namespace (host/guest)!
*/
Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2)
{
size_t length_min;
bool is_prefix;
char sentinel;
if (!length1 || !length2)
return PATHS_ARE_NOT_COMPARABLE;
/* Remove potential trailing '/' for the comparison. */
if (path1[length1 - 1] == '/')
length1--;
if (path2[length2 - 1] == '/')
length2--;
if (length1 < length2) {
length_min = length1;
sentinel = path2[length_min];
}
else {
length_min = length2;
sentinel = path1[length_min];
}
/* Optimize obvious cases. */
if (sentinel != '/' && sentinel != '\0')
return PATHS_ARE_NOT_COMPARABLE;
is_prefix = (strncmp(path1, path2, length_min) == 0);
if (!is_prefix)
return PATHS_ARE_NOT_COMPARABLE;
if (length1 == length2)
return PATHS_ARE_EQUAL;
else if (length1 < length2)
return PATH1_IS_PREFIX;
else if (length1 > length2)
return PATH2_IS_PREFIX;
return PATHS_ARE_NOT_COMPARABLE;
}
Comparison compare_paths(const char *path1, const char *path2)
{
return compare_paths2(path1, strlen(path1), path2, strlen(path2));
}
typedef int (*foreach_fd_t)(const Tracee *tracee, int fd, char path[PATH_MAX]);
/**
* Call @callback on each open file descriptors of @pid. It returns
* the status of the first failure, that is, if @callback returned
* seomthing lesser than 0, otherwise 0.
*/
static int foreach_fd(const Tracee *tracee, foreach_fd_t callback)
{
struct dirent *dirent;
char path[PATH_MAX];
char proc_fd[32]; /* 32 > sizeof("/proc//fd") + sizeof(#ULONG_MAX) */
int status;
DIR *dirp;
/* Format the path to the "virtual" directory. */
status = snprintf(proc_fd, sizeof(proc_fd), "/proc/%d/fd", tracee->pid);
if (status < 0 || (size_t) status >= sizeof(proc_fd))
return 0;
/* Open the virtual directory "/proc/$pid/fd". */
dirp = opendir(proc_fd);
if (dirp == NULL)
return 0;
while ((dirent = readdir(dirp)) != NULL) {
/* Read the value of this "virtual" link. Don't use
* readlinkat(2) here since it would require Linux >=
* 2.6.16 and Glibc >= 2.4, whereas PRoot is supposed
* to work on any Linux 2.6 systems. */
char tmp[PATH_MAX];
if (strlen(proc_fd) + strlen(dirent->d_name) + 1 >= PATH_MAX)
continue;
strcpy(tmp, proc_fd);
strcat(tmp, "/");
strcat(tmp, dirent->d_name);
status = readlink(tmp, path, PATH_MAX);
if (status < 0 || status >= PATH_MAX)
continue;
path[status] = '\0';
/* Ensure it points to a path (not a socket or somethink like that). */
if (path[0] != '/')
continue;
status = callback(tracee, atoi(dirent->d_name), path);
if (status < 0)
goto end;
}
status = 0;
end:
closedir(dirp);
return status;
}
/**
* Helper for list_open_fd().
*/
static int list_open_fd_callback(const Tracee *tracee, int fd, char path[PATH_MAX])
{
VERBOSE(tracee, 1, "pid %d: access to \"%s\" (fd %d) won't be translated until closed",
tracee->pid, path, fd);
return 0;
}
/**
* Warn for files that are open. It is useful right after PRoot has
* attached a process.
*/
int list_open_fd(const Tracee *tracee)
{
return foreach_fd(tracee, list_open_fd_callback);
}
/**
* Substitute the first @old_prefix_length bytes of @path with
* @new_prefix (the caller has to provides a correct
* @new_prefix_length). This function returns the new length of
* @path. Note: this function takes care about special cases (like
* "/").
*/
size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length,
const char *new_prefix, size_t new_prefix_length)
{
size_t path_length;
size_t new_length;
path_length = strlen(path);
assert(old_prefix_length < PATH_MAX);
assert(new_prefix_length < PATH_MAX);
if (new_prefix_length == 1) {
/* Special case: "/foo" -> "/". Substitute "/foo/bin"
* with "/bin" not "//bin". */
new_length = path_length - old_prefix_length;
if (new_length != 0)
memmove(path, path + old_prefix_length, new_length);
else {
/* Special case: "/". */
path[0] = '/';
new_length = 1;
}
}
else if (old_prefix_length == 1) {
/* Special case: "/" -> "/foo". Substitute "/bin" with
* "/foo/bin" not "/foobin". */
new_length = new_prefix_length + path_length;
if (new_length >= PATH_MAX)
return -ENAMETOOLONG;
if (path_length > 1) {
memmove(path + new_prefix_length, path, path_length);
memcpy(path, new_prefix, new_prefix_length);
}
else {
/* Special case: "/". */
memcpy(path, new_prefix, new_prefix_length);
new_length = new_prefix_length;
}
}
else {
/* Generic case. */
new_length = path_length - old_prefix_length + new_prefix_length;
if (new_length >= PATH_MAX)
return -ENAMETOOLONG;
memmove(path + new_prefix_length,
path + old_prefix_length,
path_length - old_prefix_length);
memcpy(path, new_prefix, new_prefix_length);
}
assert(new_length < PATH_MAX);
path[new_length] = '\0';
return new_length;
}
+99
View File
@@ -0,0 +1,99 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef PATH_H
#define PATH_H
#include <sys/types.h> /* pid_t, */
#include <fcntl.h> /* AT_FDCWD, */
#include <limits.h> /* PATH_MAX, */
#include <stdbool.h>
#include "tracee/tracee.h"
/* File type. */
typedef enum {
REGULAR,
SYMLINK,
} Type;
/* Path point-of-view. */
typedef enum {
GUEST,
HOST,
/* Used for bindings as specified by the user but not
* canonicalized yet (new_binding, initialize_binding). */
PENDING,
} Side;
/* Path with cached attributes. */
typedef struct {
char path[PATH_MAX];
size_t length;
Side side;
} Path;
/* Path ending type. */
typedef enum {
NOT_FINAL,
FINAL_NORMAL,
FINAL_SLASH,
FINAL_DOT
} Finality;
#define IS_FINAL(a) ((a) != NOT_FINAL)
/* Comparison between two paths. */
typedef enum Comparison {
PATHS_ARE_EQUAL,
PATH1_IS_PREFIX,
PATH2_IS_PREFIX,
PATHS_ARE_NOT_COMPARABLE,
} Comparison;
extern int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], const char *command);
extern int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final);
extern int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]);
extern void chop_finality(char *path);
extern int translate_path(Tracee *tracee, char host_path[PATH_MAX],
int dir_fd, const char *guest_path, bool deref_final);
extern int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]);
extern bool belongs_to_guestfs(const Tracee *tracee, const char *path);
extern void join_paths(char result[PATH_MAX], const char *path1, const char *path2);
extern int list_open_fd(const Tracee *tracee);
extern Comparison compare_paths(const char *path1, const char *path2);
extern Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2);
extern size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length,
const char *new_prefix, size_t new_prefix_length);
extern int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]);
/* Check if path interpretable relatively to dirfd, see openat(2) for details. */
#define AT_FD(dirfd, path) ((dirfd) != AT_FDCWD && ((path) != NULL && (path)[0] != '/'))
#endif /* PATH_H */
+189
View File
@@ -0,0 +1,189 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stdio.h> /* snprintf(3), */
#include <string.h> /* strcmp(3), */
#include <stdlib.h> /* atoi(3), strtol(3), */
#include <errno.h> /* E*, */
#include <assert.h> /* assert(3), */
#include "path/proc.h"
#include "tracee/tracee.h"
#include "path/path.h"
#include "path/binding.h"
/**
* This function emulates the @result of readlink("@base/@component")
* with respect to @tracee, where @base belongs to "/proc" (according
* to @comparison). This function returns -errno on error, an enum
* @action otherwise (c.f. above).
*
* Unlike readlink(), this function includes the nul terminating byte
* to @result.
*/
Action readlink_proc(const Tracee *tracee, char result[PATH_MAX],
const char base[PATH_MAX], const char component[NAME_MAX],
Comparison comparison)
{
const Tracee *known_tracee;
char proc_path[64]; /* 64 > sizeof("/proc//fd/") + 2 * sizeof(#ULONG_MAX) */
int status;
pid_t pid;
/* Remember: comparison = compare_paths("/proc", base) */
switch (comparison) {
case PATHS_ARE_EQUAL:
/* Substitute "/proc/self" with "/proc/<PID>". */
if (strcmp(component, "self") != 0)
return DEFAULT;
status = snprintf(result, PATH_MAX, "/proc/%d", tracee->pid);
if (status < 0 || status >= PATH_MAX)
return -EPERM;
return CANONICALIZE;
case PATH1_IS_PREFIX:
/* Handle "/proc/<PID>" below, where <PID> is process
* monitored by PRoot. */
break;
default:
return DEFAULT;
}
pid = atoi(base + strlen("/proc/"));
if (pid == 0)
return DEFAULT;
/* Handle links in "/proc/<PID>/". */
status = snprintf(proc_path, sizeof(proc_path), "/proc/%d", pid);
if (status < 0 || (size_t) status >= sizeof(proc_path))
return -EPERM;
comparison = compare_paths(proc_path, base);
switch (comparison) {
case PATHS_ARE_EQUAL:
known_tracee = get_tracee(tracee, pid, false);
if (known_tracee == NULL)
return DEFAULT;
#define SUBSTITUTE(name, string) \
do { \
if (strcmp(component, #name) != 0) \
break; \
\
status = strlen(string); \
if (status >= PATH_MAX) \
return -EPERM; \
\
strncpy(result, string, status + 1); \
return CANONICALIZE; \
} while (0)
/* Substitute link "/proc/<PID>/???" with the content
* of tracee->???. */
SUBSTITUTE(exe, known_tracee->exe);
SUBSTITUTE(cwd, known_tracee->fs->cwd);
SUBSTITUTE(root, get_root(known_tracee));
#undef SUBSTITUTE
return DEFAULT;
case PATH1_IS_PREFIX:
/* Handle "/proc/<PID>/???" below. */
break;
default:
return DEFAULT;
}
/* Handle links in "/proc/<PID>/fd/". */
status = snprintf(proc_path, sizeof(proc_path), "/proc/%d/fd", pid);
if (status < 0 || (size_t) status >= sizeof(proc_path))
return -EPERM;
comparison = compare_paths(proc_path, base);
switch (comparison) {
char *end_ptr;
case PATHS_ARE_EQUAL:
/* Sanity check: a number is expected. */
errno = 0;
(void) strtol(component, &end_ptr, 10);
if (errno != 0 || end_ptr == component)
return -EPERM;
/* Don't dereference "/proc/<PID>/fd/???" now: they
* can point to anonymous pipe, socket, ... otherwise
* they point to a path already canonicalized by the
* kernel.
*
* Note they are still correctly detranslated in
* syscall/exit.c if a monitored process uses
* readlink() against any of them. */
status = snprintf(result, PATH_MAX, "%s/%s", base, component);
if (status < 0 || status >= PATH_MAX)
return -EPERM;
return DONT_CANONICALIZE;
default:
break;
}
return DEFAULT;
}
/**
* This function emulates the @result of readlink("@referer") with
* respect to @tracee, where @referer is a strict subpath of "/proc".
* This function returns -errno if an error occured, the length of
* @result if the readlink was emulated, 0 otherwise.
*
* Unlike readlink(), this function includes the nul terminating byte
* to @result (but this byte is not counted in the returned value).
*/
ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char referer[PATH_MAX])
{
Action action;
char base[PATH_MAX];
char *component;
assert(compare_paths("/proc", referer) == PATH1_IS_PREFIX);
/* It's safe to use strrchr() here since @referer was
* previously canonicalized. */
strcpy(base, referer);
component = strrchr(base, '/');
/* These cases are not possible: @referer is supposed to be a
* canonicalized subpath of "/proc". */
assert(component != NULL && component != base);
component[0] = '\0';
component++;
if (component[0] == '\0')
return 0;
action = readlink_proc(tracee, result, base, component, PATH1_IS_PREFIX);
return (action == CANONICALIZE ? strlen(result) : 0);
}
+44
View File
@@ -0,0 +1,44 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef PROC_H
#define PROC_H
#include <limits.h>
#include "tracee/tracee.h"
#include "path/path.h"
/* Action to do after a call to readlink_proc(). */
typedef enum {
DEFAULT, /* Nothing special to do, treat it as a regular link. */
CANONICALIZE, /* The symlink was dereferenced, now canonicalize it. */
DONT_CANONICALIZE, /* The symlink shouldn't be dereferenced nor canonicalized. */
} Action;
extern Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX],
const char component[NAME_MAX], Comparison comparison);
extern ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX]);
#endif /* PROC_H */
+354
View File
@@ -0,0 +1,354 @@
#include <sys/types.h> /* stat(2), opendir(3), */
#include <sys/stat.h> /* stat(2), chmod(2), */
#include <unistd.h> /* stat(2), rmdir(2), unlink(2), readlink(2), */
#include <errno.h> /* errno(2), */
#include <dirent.h> /* readdir(3), opendir(3), */
#include <string.h> /* strcmp(3), */
#include <stdlib.h> /* free(3), getenv(3), */
#include <stdio.h> /* P_tmpdir, */
#include <talloc.h> /* talloc(3), */
#include "cli/note.h"
/**
* Return the path to a directory where temporary files should be
* created.
*/
const char *get_temp_directory()
{
static const char *temp_directory = NULL;
char *tmp;
if (temp_directory != NULL)
return temp_directory;
temp_directory = getenv("PROOT_TMP_DIR");
if (temp_directory == NULL) {
temp_directory = P_tmpdir;
return temp_directory;
}
tmp = realpath(temp_directory, NULL);
if (tmp == NULL) {
note(NULL, WARNING, SYSTEM,
"can't canonicalize %s, using %s instead of PROOT_TMP_DIR",
temp_directory, P_tmpdir);
temp_directory = P_tmpdir;
return temp_directory;
}
temp_directory = talloc_strdup(talloc_autofree_context(), tmp);
if (temp_directory == NULL)
temp_directory = tmp;
else
free(tmp);
return temp_directory;
}
/**
* Handle the return of d_type = DT_UNKNOWN by readdir(3)
* Not all filesystems support returning d_type in readdir(3)
*/
static int get_dtype(struct dirent *de)
{
int dtype = de ? de->d_type : DT_UNKNOWN;
struct stat st;
if (dtype != DT_UNKNOWN)
return dtype;
if (lstat(de->d_name, &st))
return dtype;
if (S_ISREG(st.st_mode))
return DT_REG;
if (S_ISDIR(st.st_mode))
return DT_DIR;
if (S_ISLNK(st.st_mode))
return DT_LNK;
return dtype;
}
/**
* Remove recursively the content of the current working directory.
* This latter has to lie in temp_directory (ie. "/tmp" on most
* systems). This function returns -1 if a fatal error occured
* (ie. the recursion must be stopped), the number of non-fatal errors
* otherwise.
*
* WARNING: this function changes the current working directory for
* the calling process.
*/
static int clean_temp_cwd()
{
const char *temp_directory = get_temp_directory();
const size_t length_temp_directory = strlen(temp_directory);
char *prefix = NULL;
int nb_errors = 0;
DIR *dir = NULL;
int status;
prefix = talloc_size(NULL, length_temp_directory + 1);
if (prefix == NULL) {
note(NULL, WARNING, INTERNAL, "can't allocate memory");
nb_errors++;
goto end;
}
/* Sanity check: ensure the current directory lies in
* "/tmp". */
status = readlink("/proc/self/cwd", prefix, length_temp_directory);
if (status < 0) {
note(NULL, WARNING, SYSTEM, "can't readlink '/proc/self/cwd'");
nb_errors++;
goto end;
}
prefix[status] = '\0';
if (strncmp(prefix, temp_directory, length_temp_directory) != 0) {
note(NULL, ERROR, INTERNAL,
"trying to remove a directory outside of '%s', "
"please report this error.\n", temp_directory);
nb_errors++;
goto end;
}
dir = opendir(".");
if (dir == NULL) {
note(NULL, WARNING, SYSTEM, "can't open '.'");
nb_errors++;
goto end;
}
while (1) {
struct dirent *entry;
errno = 0;
entry = readdir(dir);
if (entry == NULL)
break;
if ( strcmp(entry->d_name, ".") == 0
|| strcmp(entry->d_name, "..") == 0)
continue;
status = chmod(entry->d_name, 0700);
if (status < 0) {
note(NULL, WARNING, SYSTEM, "cant chmod '%s'", entry->d_name);
nb_errors++;
continue;
}
if (get_dtype(entry) == DT_DIR) {
status = chdir(entry->d_name);
if (status < 0) {
note(NULL, WARNING, SYSTEM, "can't chdir '%s'", entry->d_name);
nb_errors++;
continue;
}
/* Recurse. */
status = clean_temp_cwd();
if (status < 0) {
nb_errors = -1;
goto end;
}
nb_errors += status;
status = chdir("..");
if (status < 0) {
note(NULL, ERROR, SYSTEM, "can't chdir to '..'");
nb_errors = -1;
goto end;
}
status = rmdir(entry->d_name);
}
else {
status = unlink(entry->d_name);
}
if (status < 0) {
note(NULL, WARNING, SYSTEM, "can't remove '%s'", entry->d_name);
nb_errors++;
continue;
}
}
if (errno != 0) {
note(NULL, WARNING, SYSTEM, "can't readdir '.'");
nb_errors++;
}
end:
TALLOC_FREE(prefix);
if (dir != NULL)
(void) closedir(dir);
return nb_errors;
}
/**
* Remove recursively @path. This latter has to be a directory lying
* in temp_directory (ie. "/tmp" on most systems). This function
* returns -1 on error, otherwise 0.
*/
static int remove_temp_directory2(const char *path)
{
int result;
int status;
char *cwd;
cwd = malloc(PATH_MAX);
getcwd(cwd, PATH_MAX);
status = chmod(path, 0700);
if (status < 0) {
note(NULL, ERROR, SYSTEM, "can't chmod '%s'", path);
result = -1;
goto end;
}
status = chdir(path);
if (status < 0) {
note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", path);
result = -1;
goto end;
}
status = clean_temp_cwd();
result = (status == 0 ? 0 : -1);
/* Try to remove path even if something went wrong. */
status = chdir("..");
if (status < 0) {
note(NULL, ERROR, SYSTEM, "can't chdir to '..'");
result = -1;
goto end;
}
status = rmdir(path);
if (status < 0) {
note(NULL, ERROR, SYSTEM, "cant remove '%s'", path);
result = -1;
goto end;
}
end:
if (cwd != NULL) {
status = chdir(cwd);
if (status < 0) {
result = -1;
note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", cwd);
}
free(cwd);
}
return result;
}
/**
* Like remove_temp_directory2() but always return 0.
*
* Note: this is a talloc destructor.
*/
static int remove_temp_directory(char *path)
{
(void) remove_temp_directory2(path);
return 0;
}
/**
* Remove the file @path. This function always returns 0.
*
* Note: this is a talloc destructor.
*/
static int remove_temp_file(char *path)
{
int status;
status = unlink(path);
if (status < 0)
note(NULL, ERROR, SYSTEM, "can't remove '%s'", path);
return 0;
}
/**
* Create a path name with the following format:
* "/tmp/@prefix-$PID-XXXXXX". The returned C string is either
* auto-freed if @context is NULL. This function returns NULL if an
* error occurred.
*/
char *create_temp_name(TALLOC_CTX *context, const char *prefix)
{
const char *temp_directory = get_temp_directory();
char *name;
if (context == NULL)
context = talloc_autofree_context();
name = talloc_asprintf(context, "%s/%s-%d-XXXXXX", temp_directory, prefix, getpid());
if (name == NULL) {
note(NULL, ERROR, INTERNAL, "can't allocate memory");
return NULL;
}
return name;
}
/**
* Create a directory that will be automatically removed either on
* PRoot termination if @context is NULL, or once its path name
* (attached to @context) is freed. This function returns NULL on
* error, otherwise the absolute path name to the created directory
* (@prefix-ed).
*/
const char *create_temp_directory(TALLOC_CTX *context, const char *prefix)
{
char *name;
name = create_temp_name(context, prefix);
if (name == NULL)
return NULL;
name = mkdtemp(name);
if (name == NULL) {
note(NULL, ERROR, SYSTEM, "can't create temporary directory");
note(NULL, INFO, USER, "Please set PROOT_TMP_DIR env. variable "
"to an alternate location (with write permission).");
return NULL;
}
talloc_set_destructor(name, remove_temp_directory);
return name;
}
/**
* Create a file that will be automatically removed either on PRoot
* termination if @context is NULL, or once its path name (attached to
* @context) is freed. This function returns NULL on error,
* otherwise the absolute path name to the created file (@prefix-ed).
*/
const char *create_temp_file(TALLOC_CTX *context, const char *prefix)
{
char *name;
int fd;
name = create_temp_name(context, prefix);
if (name == NULL)
return NULL;
fd = mkstemp(name);
if (fd < 0) {
note(NULL, ERROR, SYSTEM, "can't create temporary file");
note(NULL, INFO, USER, "Please set PROOT_TMP_DIR env. variable "
"to an alternate location (with write permission).");
return NULL;
}
close(fd);
talloc_set_destructor(name, remove_temp_file);
return name;
}
+33
View File
@@ -0,0 +1,33 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef TEMP_H
#define TEMP_H
#include <talloc.h>
extern char *create_temp_name(TALLOC_CTX *context, const char *prefix);
extern const char *create_temp_directory(TALLOC_CTX *context, const char *prefix);
extern const char *create_temp_file(TALLOC_CTX *context, const char *prefix);
extern const char *get_temp_directory();
#endif /* TEMP_H */
+575
View File
@@ -0,0 +1,575 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/ptrace.h> /* PTRACE_*, */
#include <errno.h> /* E*, */
#include <assert.h> /* assert(3), */
#include <stdbool.h> /* bool, true, false, */
#include <signal.h> /* siginfo_t, */
#include <sys/uio.h> /* struct iovec, */
#include <sys/param.h> /* MIN(), MAX(), */
#include <sys/wait.h> /* __WALL, */
#include <string.h> /* memcpy(3), */
#include <strings.h> /* bzero(3), */
#include "ptrace/ptrace.h"
#include "tracee/tracee.h"
#include "syscall/sysnum.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
#include "tracee/event.h"
#include "cli/note.h"
#include "arch.h"
#include "compat.h"
#if defined(ARCH_ARM_EABI)
#define user_fpregs_struct user_fpregs
#endif
#if defined(ARCH_ARM64)
#define user_fpregs_struct user_fpsimd_struct
#endif
static const char *stringify_ptrace(int request)
{
#define CASE_STR(a) case a: return #a; break;
switch ((int) request) {
CASE_STR(PTRACE_TRACEME) CASE_STR(PTRACE_PEEKTEXT) CASE_STR(PTRACE_PEEKDATA)
CASE_STR(PTRACE_PEEKUSER) CASE_STR(PTRACE_POKETEXT) CASE_STR(PTRACE_POKEDATA)
CASE_STR(PTRACE_POKEUSER) CASE_STR(PTRACE_CONT) CASE_STR(PTRACE_KILL)
CASE_STR(PTRACE_SINGLESTEP) CASE_STR(PTRACE_GETREGS) CASE_STR(PTRACE_SETREGS)
CASE_STR(PTRACE_GETFPREGS) CASE_STR(PTRACE_SETFPREGS) CASE_STR(PTRACE_ATTACH)
CASE_STR(PTRACE_DETACH) CASE_STR(PTRACE_GETFPXREGS) CASE_STR(PTRACE_SETFPXREGS)
CASE_STR(PTRACE_SYSCALL) CASE_STR(PTRACE_SETOPTIONS) CASE_STR(PTRACE_GETEVENTMSG)
CASE_STR(PTRACE_GETSIGINFO) CASE_STR(PTRACE_SETSIGINFO) CASE_STR(PTRACE_GETREGSET)
CASE_STR(PTRACE_SETREGSET) CASE_STR(PTRACE_SEIZE) CASE_STR(PTRACE_INTERRUPT)
CASE_STR(PTRACE_LISTEN) CASE_STR(PTRACE_SET_SYSCALL)
CASE_STR(PTRACE_GET_THREAD_AREA) CASE_STR(PTRACE_SET_THREAD_AREA)
CASE_STR(PTRACE_GETVFPREGS) CASE_STR(PTRACE_SINGLEBLOCK) CASE_STR(PTRACE_ARCH_PRCTL)
default: return "PTRACE_???"; }
}
/**
* Translate the ptrace syscall made by @tracee into a "void" syscall
* in order to emulate the ptrace mechanism within PRoot. This
* function returns -errno if an error occured (unsupported request),
* otherwise 0.
*/
int translate_ptrace_enter(Tracee *tracee)
{
/* The ptrace syscall have to be emulated since it can't be nested. */
set_sysnum(tracee, PR_void);
return 0;
}
/**
* Set @ptracee's tracer to @ptracer, and increment ptracees counter
* of this later.
*/
void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer)
{
bzero(&(PTRACEE), sizeof(PTRACEE));
PTRACEE.ptracer = ptracer;
PTRACER.nb_ptracees++;
}
/**
* Unset @ptracee's tracer, and decrement ptracees counter of this
* later.
*/
void detach_from_ptracer(Tracee *ptracee)
{
Tracee *ptracer = PTRACEE.ptracer;
PTRACEE.ptracer = NULL;
assert(PTRACER.nb_ptracees > 0);
PTRACER.nb_ptracees--;
}
/**
* Emulate the ptrace syscall made by @tracee. This function returns
* -errno if an error occured (unsupported request), otherwise 0.
*/
int translate_ptrace_exit(Tracee *tracee)
{
word_t request, pid, address, data, result;
Tracee *ptracee, *ptracer;
int forced_signal = -1;
int signal;
int status;
/* Read ptrace parameters. */
request = peek_reg(tracee, ORIGINAL, SYSARG_1);
pid = peek_reg(tracee, ORIGINAL, SYSARG_2);
address = peek_reg(tracee, ORIGINAL, SYSARG_3);
data = peek_reg(tracee, ORIGINAL, SYSARG_4);
/* Propagate signedness for this special value. */
if (is_32on64_mode(tracee) && pid == 0xFFFFFFFF)
pid = (word_t) -1;
/* The TRACEME request is the only one used by a tracee. */
if (request == PTRACE_TRACEME) {
ptracer = tracee->parent;
ptracee = tracee;
/* The emulated ptrace in PRoot has the same
* limitation as the real ptrace in the Linux kernel:
* only one tracer per process. */
if (PTRACEE.ptracer != NULL || ptracee == ptracer)
return -EPERM;
attach_to_ptracer(ptracee, ptracer);
/* Detect when the ptracer has gone to wait before the
* ptracee did the ptrace(ATTACHME) request. */
if (PTRACER.waits_in == WAITS_IN_KERNEL) {
status = kill(ptracer->pid, SIGSTOP);
if (status < 0)
note(tracee, WARNING, INTERNAL,
"can't wake ptracer %d", ptracer->pid);
else {
ptracer->sigstop = SIGSTOP_IGNORED;
PTRACER.waits_in = WAITS_IN_PROOT;
}
}
/* Disable seccomp acceleration for this tracee and
* all its children since we can't assume what are the
* syscalls its tracer is interested with. */
if (tracee->seccomp == ENABLED)
tracee->seccomp = DISABLING;
return 0;
}
/* The ATTACH, SEIZE, and INTERRUPT requests are the only ones
* where the ptracee is in an unknown state. */
if (request == PTRACE_ATTACH) {
ptracer = tracee;
ptracee = get_tracee(ptracer, pid, false);
if (ptracee == NULL)
return -ESRCH;
/* The emulated ptrace in PRoot has the same
* limitation as the real ptrace in the Linux kernel:
* only one tracer per process. */
if (PTRACEE.ptracer != NULL || ptracee == ptracer)
return -EPERM;
attach_to_ptracer(ptracee, ptracer);
/* The tracee is sent a SIGSTOP, but will not
* necessarily have stopped by the completion of this
* call.
*
* -- man 2 ptrace. */
kill(pid, SIGSTOP);
return 0;
}
/* Here, the tracee is a ptracer. Also, the requested ptracee
* has to be in the "stopped for ptracer" state. */
ptracer = tracee;
ptracee = get_stopped_ptracee(ptracer, pid, false, __WALL);
if (ptracee == NULL) {
static bool warned = false;
/* Ensure we didn't get there only because inheritance
* mechanism has missed this one. */
ptracee = get_tracee(tracee, pid, false);
if (ptracee != NULL && ptracee->exe == NULL && !warned) {
warned = true;
note(ptracer, WARNING, INTERNAL, "ptrace request to an unexpected ptracee");
}
return -ESRCH;
}
/* Sanity checks. */
if ( PTRACEE.is_zombie
|| PTRACEE.ptracer != ptracer
|| pid == (word_t) -1)
return -ESRCH;
switch (request) {
case PTRACE_SYSCALL:
PTRACEE.ignore_syscalls = false;
forced_signal = (int) data;
status = 0;
break; /* Restart the ptracee. */
case PTRACE_CONT:
PTRACEE.ignore_syscalls = true;
forced_signal = (int) data;
status = 0;
break; /* Restart the ptracee. */
case PTRACE_SINGLESTEP:
ptracee->restart_how = PTRACE_SINGLESTEP;
forced_signal = (int) data;
status = 0;
break; /* Restart the ptracee. */
case PTRACE_SINGLEBLOCK:
ptracee->restart_how = PTRACE_SINGLEBLOCK;
forced_signal = (int) data;
status = 0;
break; /* Restart the ptracee. */
case PTRACE_DETACH:
detach_from_ptracer(ptracee);
status = 0;
break; /* Restart the ptracee. */
case PTRACE_KILL:
status = ptrace(request, pid, NULL, NULL);
break; /* Restart the ptracee. */
case PTRACE_SETOPTIONS:
PTRACEE.options = data;
return 0; /* Don't restart the ptracee. */
case PTRACE_GETEVENTMSG: {
status = ptrace(request, pid, NULL, &result);
if (status < 0)
return -errno;
poke_word(ptracer, data, result);
if (errno != 0)
return -errno;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_PEEKUSER:
if (is_32on64_mode(ptracer) && address == (word_t) -1)
return -EIO;
/* Fall through. */
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
errno = 0;
result = (word_t) ptrace(request, pid, address, NULL);
if (errno != 0)
return -errno;
poke_word(ptracer, data, result);
if (errno != 0)
return -errno;
return 0; /* Don't restart the ptracee. */
case PTRACE_POKEUSER:
if (is_32on64_mode(ptracer)) {
if (address == (word_t) -1)
return -EIO;
}
status = ptrace(request, pid, address, data);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
if (is_32on64_mode(ptracer)) {
word_t tmp;
errno = 0;
tmp = (word_t) ptrace(PTRACE_PEEKDATA, ptracee->pid, address, NULL);
if (errno != 0)
return -errno;
data |= (tmp & 0xFFFFFFFF00000000ULL);
}
status = ptrace(request, pid, address, data);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
case PTRACE_GETSIGINFO: {
siginfo_t siginfo;
status = ptrace(request, pid, NULL, &siginfo);
if (status < 0)
return -errno;
status = write_data(ptracer, data, &siginfo, sizeof(siginfo));
if (status < 0)
return status;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_SETSIGINFO: {
siginfo_t siginfo;
status = read_data(ptracer, &siginfo, data, sizeof(siginfo));
if (status < 0)
return status;
status = ptrace(request, pid, NULL, &siginfo);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_GETREGS: {
size_t size;
union {
struct user_regs_struct regs;
uint32_t regs32[0];
} buffer;
status = ptrace(request, pid, NULL, &buffer);
if (status < 0)
return -errno;
if (is_32on64_mode(tracee)) {
struct user_regs_struct regs64;
memcpy(&regs64, &buffer.regs, sizeof(struct user_regs_struct));
size = sizeof(buffer.regs32);
}
else
size = sizeof(buffer.regs);
status = write_data(ptracer, data, &buffer, size);
if (status < 0)
return status;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_SETREGS: {
size_t size;
union {
struct user_regs_struct regs;
uint32_t regs32[0];
} buffer;
size = (is_32on64_mode(ptracer)
? sizeof(buffer.regs32)
: sizeof(buffer.regs));
status = read_data(ptracer, &buffer, data, size);
if (status < 0)
return status;
if (is_32on64_mode(ptracer)) {
uint32_t regs32[0];
memcpy(regs32, buffer.regs32, sizeof(regs32));
}
status = ptrace(request, pid, NULL, &buffer);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_GETFPREGS: {
size_t size;
union {
struct user_fpregs_struct fpregs;
uint32_t fpregs32[0];
} buffer;
status = ptrace(request, pid, NULL, &buffer);
if (status < 0)
return -errno;
if (is_32on64_mode(tracee)) {
static bool warned = false;
if (!warned)
note(ptracer, WARNING, INTERNAL,
"ptrace 32-bit request '%s' not supported on 64-bit yet",
stringify_ptrace(request));
warned = true;
bzero(&buffer, sizeof(buffer));
size = sizeof(buffer.fpregs32);
}
else
size = sizeof(buffer.fpregs);
status = write_data(ptracer, data, &buffer, size);
if (status < 0)
return status;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_SETFPREGS: {
size_t size;
union {
struct user_fpregs_struct fpregs;
uint32_t fpregs32[0];
} buffer;
size = (is_32on64_mode(ptracer)
? sizeof(buffer.fpregs32)
: sizeof(buffer.fpregs));
status = read_data(ptracer, &buffer, data, size);
if (status < 0)
return status;
if (is_32on64_mode(ptracer)) {
static bool warned = false;
if (!warned)
note(ptracer, WARNING, INTERNAL,
"ptrace 32-bit request '%s' not supported on 64-bit yet",
stringify_ptrace(request));
warned = true;
return -ENOTSUP;
}
status = ptrace(request, pid, NULL, &buffer);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_GETREGSET: {
struct iovec local_iovec;
word_t remote_iovec_base;
word_t remote_iovec_len;
remote_iovec_base = peek_word(ptracer, data);
if (errno != 0)
return -errno;
remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer));
if (errno != 0)
return -errno;
/* Sanity check. */
assert(sizeof(local_iovec.iov_len) == sizeof(word_t));
local_iovec.iov_len = remote_iovec_len;
local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len);
if (local_iovec.iov_base == NULL)
return -ENOMEM;
status = ptrace(PTRACE_GETREGSET, pid, address, &local_iovec);
if (status < 0)
return status;
remote_iovec_len = local_iovec.iov_len =
MIN(remote_iovec_len, local_iovec.iov_len);
/* Update remote vector content. */
status = writev_data(ptracer, remote_iovec_base, &local_iovec, 1);
if (status < 0)
return status;
/* Update remote vector length. */
poke_word(ptracer, data + sizeof_word(ptracer), remote_iovec_len);
if (errno != 0)
return -errno;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_SETREGSET: {
struct iovec local_iovec;
word_t remote_iovec_base;
word_t remote_iovec_len;
remote_iovec_base = peek_word(ptracer, data);
if (errno != 0)
return -errno;
remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer));
if (errno != 0)
return -errno;
/* Sanity check. */
assert(sizeof(local_iovec.iov_len) == sizeof(word_t));
local_iovec.iov_len = remote_iovec_len;
local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len);
if (local_iovec.iov_base == NULL)
return -ENOMEM;
/* Copy remote content into the local vector. */
status = read_data(ptracer, local_iovec.iov_base,
remote_iovec_base, local_iovec.iov_len);
if (status < 0)
return status;
status = ptrace(PTRACE_SETREGSET, pid, address, &local_iovec);
if (status < 0)
return status;
return 0; /* Don't restart the ptracee. */
}
case PTRACE_GETVFPREGS:
case PTRACE_GETFPXREGS: {
static bool warned = false;
if (!warned)
note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet",
stringify_ptrace(request));
warned = true;
return -ENOTSUP;
}
case PTRACE_SET_SYSCALL:
status = ptrace(request, pid, address, data);
if (status < 0)
return -errno;
return 0; /* Don't restart the ptracee. */
default:
note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet",
stringify_ptrace(request));
return -ENOTSUP;
}
/* Now, the initial tracee's event can be handled. */
signal = PTRACEE.event4.proot.pending
? handle_tracee_event(ptracee, PTRACEE.event4.proot.value)
: PTRACEE.event4.proot.value;
/* The restarting signal from the ptracer overrides the
* restarting signal from PRoot. */
if (forced_signal != -1)
signal = forced_signal;
(void) restart_tracee(ptracee, signal);
return status;
}
@@ -0,0 +1,36 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef PTRACE_H
#define PTRACE_H
#include "tracee/tracee.h"
extern int translate_ptrace_enter(Tracee *tracee);
extern int translate_ptrace_exit(Tracee *tracee);
extern void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer);
extern void detach_from_ptracer(Tracee *ptracee);
#define PTRACEE (ptracee->as_ptracee)
#define PTRACER (ptracer->as_ptracer)
#endif /* PTRACE_H */
+393
View File
@@ -0,0 +1,393 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/ptrace.h> /* PTRACE_*, */
#include <errno.h> /* E*, */
#include <assert.h> /* assert(3), */
#include <stdbool.h> /* bool, true, false, */
#include <signal.h> /* SIG*, */
#include <talloc.h> /* talloc*, */
#include "ptrace/wait.h"
#include "ptrace/ptrace.h"
#include "syscall/sysnum.h"
#include "syscall/chain.h"
#include "tracee/tracee.h"
#include "tracee/event.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "attribute.h"
static const char *stringify_event(int event) UNUSED;
static const char *stringify_event(int event)
{
if (WIFEXITED(event))
return "exited";
else if (WIFSIGNALED(event))
return "signaled";
else if (WIFCONTINUED(event))
return "continued";
else if (WIFSTOPPED(event)) {
switch ((event & 0xfff00) >> 8) {
case SIGTRAP:
return "stopped: SIGTRAP";
case SIGTRAP | 0x80:
return "stopped: SIGTRAP: 0x80";
case SIGTRAP | PTRACE_EVENT_VFORK << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_VFORK";
case SIGTRAP | PTRACE_EVENT_FORK << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_FORK";
case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_VFORK_DONE";
case SIGTRAP | PTRACE_EVENT_CLONE << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_CLONE";
case SIGTRAP | PTRACE_EVENT_EXEC << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_EXEC";
case SIGTRAP | PTRACE_EVENT_EXIT << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_EXIT";
case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP2";
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8:
return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP";
case SIGSTOP:
return "stopped: SIGSTOP";
default:
return "stopped: unknown";
}
}
return "unknown";
}
/**
* Translate the wait syscall made by @ptracer into a "void" syscall
* if the expected pid is one of its ptracees, in order to emulate the
* ptrace mechanism within PRoot. This function returns -errno if an
* error occured (unsupported request), otherwise 0.
*/
int translate_wait_enter(Tracee *ptracer)
{
Tracee *ptracee;
pid_t pid;
PTRACER.waits_in = WAITS_IN_KERNEL;
/* Don't emulate the ptrace mechanism if it's not a ptracer. */
if (PTRACER.nb_ptracees == 0)
return 0;
/* Don't emulate the ptrace mechanism if the requested pid is
* not a ptracee. */
pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1);
if (pid != -1) {
ptracee = get_tracee(ptracer, pid, false);
if (ptracee == NULL || PTRACEE.ptracer != ptracer)
return 0;
}
/* This syscall is canceled at the enter stage in order to be
* handled at the exit stage. */
set_sysnum(ptracer, PR_void);
PTRACER.waits_in = WAITS_IN_PROOT;
return 0;
}
/**
* Update pid & wait status of @ptracer's wait(2) for the given
* @ptracee. This function returns -errno if an error occurred, 0 if
* the wait syscall will be restarted (ie. the event is discarded),
* otherwise @ptracee's pid.
*/
static int update_wait_status(Tracee *ptracer, Tracee *ptracee)
{
word_t address;
int result;
/* Special case: the Linux kernel reports the terminating
* event issued by a process to both its parent and its
* tracer, except when they are the same. In this case the
* Linux kernel reports the terminating event only once to the
* tracing parent ... */
if (PTRACEE.ptracer == ptracee->parent
&& (WIFEXITED(PTRACEE.event4.ptracer.value)
|| WIFSIGNALED(PTRACEE.event4.ptracer.value))) {
/* ... So hide this terminating event (toward its
* tracer, ie. PRoot) and make the second one appear
* (towards its parent, ie. the ptracer). This will
* ensure its exit status is collected from a kernel
* point-of-view (ie. it doesn't stay a zombie
* forever). */
restart_original_syscall(ptracer);
/* Detach this ptracee from its ptracer, PRoot doesn't
* have anything else to emulate. */
detach_from_ptracer(ptracee);
/* Zombies can rest in peace once the ptracer is
* notified. */
if (PTRACEE.is_zombie)
TALLOC_FREE(ptracee);
return 0;
}
address = peek_reg(ptracer, ORIGINAL, SYSARG_2);
if (address != 0) {
poke_int32(ptracer, address, PTRACEE.event4.ptracer.value);
if (errno != 0)
return -errno;
}
PTRACEE.event4.ptracer.pending = false;
/* Be careful; ptracee might get freed before its pid is
* returned. */
result = ptracee->pid;
/* Zombies can rest in peace once the ptracer is notified. */
if (PTRACEE.is_zombie) {
detach_from_ptracer(ptracee);
TALLOC_FREE(ptracee);
}
return result;
}
/**
* Emulate the wait* syscall made by @ptracer if it was in the context
* of the ptrace mechanism. This function returns -errno if an error
* occured, otherwise the pid of the expected tracee.
*/
int translate_wait_exit(Tracee *ptracer)
{
Tracee *ptracee;
word_t options;
int status;
pid_t pid;
assert(PTRACER.waits_in == WAITS_IN_PROOT);
PTRACER.waits_in = DOESNT_WAIT;
pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1);
options = peek_reg(ptracer, ORIGINAL, SYSARG_3);
/* Is there such a stopped ptracee with an event not yet
* passed to its ptracer? */
ptracee = get_stopped_ptracee(ptracer, pid, true, options);
if (ptracee == NULL) {
/* Is there still living ptracees? */
if (PTRACER.nb_ptracees == 0)
return -ECHILD;
/* Non blocking wait(2) ? */
if ((options & WNOHANG) != 0) {
/* if WNOHANG was specified and one or more
* child(ren) specified by pid exist, but have
* not yet changed state, then 0 is returned.
* On error, -1 is returned.
*
* -- man 2 waitpid */
return (has_ptracees(ptracer, pid, options) ? 0 : -ECHILD);
}
/* Otherwise put this ptracer in the "waiting for
* ptracee" state, it will be woken up in
* handle_ptracee_event() later. */
PTRACER.wait_pid = pid;
PTRACER.wait_options = options;
return 0;
}
status = update_wait_status(ptracer, ptracee);
if (status < 0)
return status;
return status;
}
/**
* For the given @ptracee, pass its current @event to its ptracer if
* this latter is waiting for it, otherwise put the @ptracee in the
* "waiting for ptracer" state. This function returns whether
* @ptracee shall be kept in the stop state or not.
*/
bool handle_ptracee_event(Tracee *ptracee, int event)
{
bool handled_by_proot_first = false;
bool handled_by_proot_first_may_suppress = false;
Tracee *ptracer = PTRACEE.ptracer;
bool keep_stopped;
assert(ptracer != NULL);
/* Remember what the event initially was, this will be
* required by PRoot to handle this event later. */
PTRACEE.event4.proot.value = event;
PTRACEE.event4.proot.pending = true;
/* By default, this ptracee should be kept stopped until its
* ptracer restarts it. */
keep_stopped = true;
/* Not all events are expected for this ptracee. */
if (WIFSTOPPED(event)) {
switch ((event & 0xfff00) >> 8) {
case SIGTRAP | 0x80:
if (PTRACEE.ignore_syscalls || PTRACEE.ignore_loader_syscalls)
return false;
if ((PTRACEE.options & PTRACE_O_TRACESYSGOOD) == 0)
event &= ~(0x80 << 8);
handled_by_proot_first = IS_IN_SYSEXIT(ptracee);
break;
#define PTRACE_EVENT_VFORKDONE PTRACE_EVENT_VFORK_DONE
#define CASE_FILTER_EVENT(name) \
case SIGTRAP | PTRACE_EVENT_ ##name << 8: \
if ((PTRACEE.options & PTRACE_O_TRACE ##name) == 0) \
return false; \
PTRACEE.tracing_started = true; \
handled_by_proot_first = true; \
break;
CASE_FILTER_EVENT(FORK);
CASE_FILTER_EVENT(VFORK);
CASE_FILTER_EVENT(VFORKDONE);
CASE_FILTER_EVENT(CLONE);
CASE_FILTER_EVENT(EXIT);
CASE_FILTER_EVENT(EXEC);
/* Never reached. */
assert(0);
case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8:
/* These events are not supported [yet?] under
* ptrace emulation. */
return false;
case SIGSYS:
handled_by_proot_first = true;
handled_by_proot_first_may_suppress = true;
break;
default:
PTRACEE.tracing_started = true;
break;
}
}
/* In these cases, the ptracee isn't really alive anymore. To
* ensure it will not be in limbo, PRoot restarts it whether
* its ptracer is waiting for it or not. */
else if (WIFEXITED(event) || WIFSIGNALED(event)) {
PTRACEE.tracing_started = true;
keep_stopped = false;
}
/* A process is not traced right from the TRACEME request; it
* is traced from the first received signal, whether it was
* raised by the process itself (implicitly or explicitly), or
* it was induced by a PTRACE_EVENT_*. */
if (!PTRACEE.tracing_started)
return false;
/* Under some circumstances, the event must be handled by
* PRoot first. */
if (handled_by_proot_first) {
int signal;
signal = handle_tracee_event(ptracee, PTRACEE.event4.proot.value);
PTRACEE.event4.proot.value = signal;
if (handled_by_proot_first_may_suppress) {
/* If we've decided to suppress signal
* (e.g. because seccomp policy blocked syscall
* but we emulate that syscall),
* don't notify ptracer and let ptracee resume. */
if (signal == 0) {
if (seccomp_event_happens_after_enter_sigtrap()) {
if (PTRACEE.ignore_syscalls)
{
restart_tracee(ptracee, 0);
return true;
}
/* However if we've already notified ptracer about syscall entry
* before knowing it'll be blocked, notify ptracer about exit. */
PTRACEE.event4.proot.value = 0;
if (PTRACEE.options & PTRACE_O_TRACESYSGOOD) {
event = (SIGTRAP | 0x80) << 8 | 0x7f;
} else {
event = SIGTRAP << 8 | 0x7f;
}
} else {
restart_tracee(ptracee, 0);
return true;
}
}
} else {
/* The computed signal is always 0 since we can come
* in this block only on sysexit and special events
* (as for now). */
assert(signal == 0);
}
}
/* Remember what the new event is, this will be required by
the ptracer in translate_ptrace_exit() in order to restart
this ptracee. */
PTRACEE.event4.ptracer.value = event;
PTRACEE.event4.ptracer.pending = true;
/* Notify asynchronously the ptracer about this event, as the
* kernel does. */
kill(ptracer->pid, SIGCHLD);
/* Note: wait_pid is set in translate_wait_exit() if no
* ptracee event was pending when the ptracer started to
* wait. */
if ( (PTRACER.wait_pid == -1 || PTRACER.wait_pid == ptracee->pid)
&& EXPECTED_WAIT_CLONE(PTRACER.wait_options, ptracee)) {
bool restarted;
int status;
status = update_wait_status(ptracer, ptracee);
if (status == 0)
chain_next_syscall(ptracer);
else
poke_reg(ptracer, SYSARG_RESULT, (word_t) status);
/* Write ptracer's register cache back. */
(void) push_regs(ptracer);
/* Restart the ptracer. */
PTRACER.wait_pid = 0;
restarted = restart_tracee(ptracer, 0);
if (!restarted)
keep_stopped = false;
return keep_stopped;
}
return keep_stopped;
}
+49
View File
@@ -0,0 +1,49 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef PTRACE_WAIT_H
#define PTRACE_WAIT_H
#include <sys/wait.h> /* for __WALL */
#include "tracee/tracee.h"
extern int translate_wait_enter(Tracee *ptracer);
extern int translate_wait_exit(Tracee *ptracer);
extern bool handle_ptracee_event(Tracee *ptracee, int wait_status);
/* __WCLONE: Wait for "clone" children only. If omitted then wait for
* "non-clone" children only. (A "clone" child is one which delivers
* no signal, or a signal other than SIGCHLD to its parent upon
* termination.) This option is ignored if __WALL is also specified.
*
* __WALL: Wait for all children, regardless of type ("clone" or
* "non-clone").
*
* -- wait(2) man-page
*/
#define EXPECTED_WAIT_CLONE(wait_options, tracee) \
((((wait_options) & __WALL) != 0) \
|| ((((wait_options) & __WCLONE) != 0) && (tracee)->clone) \
|| ((((wait_options) & __WCLONE) == 0) && !(tracee)->clone))
#endif /* PTRACE_WAIT_H */
+185
View File
@@ -0,0 +1,185 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <talloc.h> /* talloc*, */
#include <sys/queue.h> /* STAILQ_*, */
#include <errno.h> /* E*, */
#include <assert.h> /* assert(3), */
#include "cli/note.h"
#include "syscall/chain.h"
#include "syscall/sysnum.h"
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "arch.h"
struct chained_syscall {
Sysnum sysnum;
word_t sysargs[6];
STAILQ_ENTRY(chained_syscall) link;
};
STAILQ_HEAD(chained_syscalls, chained_syscall);
static int register_chained_syscall_internal(Tracee *tracee, Sysnum sysnum,
word_t sysarg_1, word_t sysarg_2, word_t sysarg_3,
word_t sysarg_4, word_t sysarg_5, word_t sysarg_6,
bool at_front)
{
struct chained_syscall *syscall;
if (tracee->chain.syscalls == NULL) {
tracee->chain.syscalls = talloc_zero(tracee, struct chained_syscalls);
if (tracee->chain.syscalls == NULL)
return -ENOMEM;
STAILQ_INIT(tracee->chain.syscalls);
}
syscall = talloc_zero(tracee->chain.syscalls, struct chained_syscall);
if (syscall == NULL)
return -ENOMEM;
syscall->sysnum = sysnum;
syscall->sysargs[0] = sysarg_1;
syscall->sysargs[1] = sysarg_2;
syscall->sysargs[2] = sysarg_3;
syscall->sysargs[3] = sysarg_4;
syscall->sysargs[4] = sysarg_5;
syscall->sysargs[5] = sysarg_6;
if (at_front) {
STAILQ_INSERT_HEAD(tracee->chain.syscalls, syscall, link);
} else {
STAILQ_INSERT_TAIL(tracee->chain.syscalls, syscall, link);
}
return 0;
}
/**
* Append a new syscall (@sysnum, @sysarg_*) to the list of
* "unrequested" syscalls for the given @tracee. These new syscalls
* will be triggered in order once the current syscall is done. The
* caller is free to force the last result of this syscall chain in
* @tracee->chain.final_result. This function returns -errno if an
* error occurred, otherwise 0.
*/
int register_chained_syscall(Tracee *tracee, Sysnum sysnum,
word_t sysarg_1, word_t sysarg_2, word_t sysarg_3,
word_t sysarg_4, word_t sysarg_5, word_t sysarg_6) {
return register_chained_syscall_internal(
tracee, sysnum,
sysarg_1, sysarg_2, sysarg_3,
sysarg_4, sysarg_5, sysarg_6,
false
);
}
/**
* Use/remove the first element of @tracee->chain.syscalls to forge a
* new syscall. This function should be called only at the end of in
* the sysexit stage.
*/
void chain_next_syscall(Tracee *tracee)
{
struct chained_syscall *syscall;
word_t instr_pointer;
word_t sysnum;
assert(tracee->chain.syscalls != NULL);
/* No more chained syscalls: force the result of the initial
* syscall (the one explicitly requested by the tracee). */
if (STAILQ_EMPTY(tracee->chain.syscalls)) {
TALLOC_FREE(tracee->chain.syscalls);
if (tracee->chain.force_final_result)
poke_reg(tracee, SYSARG_RESULT, tracee->chain.final_result);
tracee->chain.force_final_result = false;
tracee->chain.final_result = 0;
VERBOSE(tracee, 2, "chain_next_syscall finish");
return;
}
VERBOSE(tracee, 2, "chain_next_syscall continue");
/* Original register values will be restored right after the
* last chained syscall. */
tracee->restore_original_regs = false;
/* The list of chained syscalls is a FIFO. */
syscall = STAILQ_FIRST(tracee->chain.syscalls);
STAILQ_REMOVE_HEAD(tracee->chain.syscalls, link);
poke_reg(tracee, SYSARG_1, syscall->sysargs[0]);
poke_reg(tracee, SYSARG_2, syscall->sysargs[1]);
poke_reg(tracee, SYSARG_3, syscall->sysargs[2]);
poke_reg(tracee, SYSARG_4, syscall->sysargs[3]);
poke_reg(tracee, SYSARG_5, syscall->sysargs[4]);
poke_reg(tracee, SYSARG_6, syscall->sysargs[5]);
sysnum = detranslate_sysnum(get_abi(tracee), syscall->sysnum);
poke_reg(tracee, SYSTRAP_NUM, sysnum);
/* Move the instruction pointer back to the original trap. */
instr_pointer = peek_reg(tracee, CURRENT, INSTR_POINTER);
poke_reg(tracee, INSTR_POINTER, instr_pointer - get_systrap_size(tracee));
/* Break after exit from syscall, there may be another one in chain */
tracee->restart_how = PTRACE_SYSCALL;
}
/**
* Restart the original syscall of the given @tracee. The result of
* the current syscall will be overwritten. This function returns the
* same status as register_chained_syscall().
*/
int restart_original_syscall(Tracee *tracee)
{
return register_chained_syscall(tracee,
get_sysnum(tracee, ORIGINAL),
peek_reg(tracee, ORIGINAL, SYSARG_1),
peek_reg(tracee, ORIGINAL, SYSARG_2),
peek_reg(tracee, ORIGINAL, SYSARG_3),
peek_reg(tracee, ORIGINAL, SYSARG_4),
peek_reg(tracee, ORIGINAL, SYSARG_5),
peek_reg(tracee, ORIGINAL, SYSARG_6));
}
int restart_current_syscall_as_chained(Tracee *tracee)
{
assert(tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_INACTIVE);
tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL;
return register_chained_syscall_internal(tracee,
get_sysnum(tracee, CURRENT),
peek_reg(tracee, CURRENT, SYSARG_1),
peek_reg(tracee, CURRENT, SYSARG_2),
peek_reg(tracee, CURRENT, SYSARG_3),
peek_reg(tracee, CURRENT, SYSARG_4),
peek_reg(tracee, CURRENT, SYSARG_5),
peek_reg(tracee, CURRENT, SYSARG_6),
true);
}
@@ -0,0 +1,41 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef CHAIN_H
#define CHAIN_H
#include "tracee/tracee.h"
#include "syscall/sysnum.h"
#include "arch.h"
extern int register_chained_syscall(Tracee *tracee, Sysnum sysnum,
word_t sysarg_1, word_t sysarg_2, word_t sysarg_3,
word_t sysarg_4, word_t sysarg_5, word_t sysarg_6);
extern int restart_original_syscall(Tracee *tracee);
extern void chain_next_syscall(Tracee *tracee);
extern int restart_current_syscall_as_chained(Tracee *tracee);
#endif /* CHAIN_H */
+466
View File
@@ -0,0 +1,466 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <errno.h> /* errno(3), E* */
#include <talloc.h> /* talloc_*, */
#include <sys/un.h> /* struct sockaddr_un, */
#include <linux/net.h> /* SYS_*, */
#include <fcntl.h> /* AT_FDCWD, */
#include <limits.h> /* PATH_MAX, */
#include <string.h> /* strcpy */
#include <sys/prctl.h> /* PR_SET_DUMPABLE */
#include <termios.h> /* TCSETS, TCSANOW */
#include "syscall/syscall.h"
#include "syscall/sysnum.h"
#include "syscall/socket.h"
#include "ptrace/ptrace.h"
#include "ptrace/wait.h"
#include "syscall/heap.h"
#include "execve/execve.h"
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
#include "path/path.h"
#include "path/canon.h"
#include "arch.h"
/**
* Translate @path and put the result in the @tracee's memory address
* space pointed to by the @reg argument of the current syscall. See
* the documentation of translate_path() about the meaning of
* @type. This function returns -errno if an error occured, otherwise
* 0.
*/
static int translate_path2(Tracee *tracee, int dir_fd, char path[PATH_MAX], Reg reg, Type type)
{
char new_path[PATH_MAX];
int status;
/* Special case where the argument was NULL. */
if (path[0] == '\0')
return 0;
/* Translate the original path. */
status = translate_path(tracee, new_path, dir_fd, path, type != SYMLINK);
if (status < 0)
return status;
return set_sysarg_path(tracee, new_path, reg);
}
/**
* A helper, see the comment of the function above.
*/
static int translate_sysarg(Tracee *tracee, Reg reg, Type type)
{
char old_path[PATH_MAX];
int status;
/* Extract the original path. */
status = get_sysarg_path(tracee, old_path, reg);
if (status < 0)
return status;
return translate_path2(tracee, AT_FDCWD, old_path, reg, type);
}
/**
* Translate the input arguments of the current @tracee's syscall in the
* @tracee->pid process area. This function sets @tracee->status to
* -errno if an error occured from the tracee's point-of-view (EFAULT
* for instance), otherwise 0.
*/
int translate_syscall_enter(Tracee *tracee)
{
int flags;
int dirfd;
int olddirfd;
int newdirfd;
int status = 0;
char path[PATH_MAX];
char oldpath[PATH_MAX];
char newpath[PATH_MAX];
word_t syscall_number;
bool special = false;
/* Translate input arguments. */
syscall_number = get_sysnum(tracee, ORIGINAL);
switch (syscall_number) {
case PR_execve:
status = translate_execve_enter(tracee);
break;
case PR_ptrace:
status = translate_ptrace_enter(tracee);
break;
case PR_wait4:
case PR_waitpid:
status = translate_wait_enter(tracee);
break;
case PR_brk:
translate_brk_enter(tracee);
status = 0;
break;
case PR_getcwd:
set_sysnum(tracee, PR_void);
status = 0;
break;
case PR_fchdir:
case PR_chdir: {
struct stat statl;
char *tmp;
/* The ending "." ensures an error will be reported if
* path does not exist or if it is not a directory. */
if (syscall_number == PR_chdir) {
status = get_sysarg_path(tracee, path, SYSARG_1);
if (status < 0)
break;
join_paths(oldpath, path, ".");
dirfd = AT_FDCWD;
}
else {
strcpy(oldpath, ".");
dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
}
status = translate_path(tracee, path, dirfd, oldpath, true);
if (status < 0)
break;
status = lstat(path, &statl);
if (status < 0)
break;
/* Check this directory is accessible. */
if ((statl.st_mode & S_IXUSR) == 0)
return -EACCES;
/* Sadly this method doesn't detranslate statefully,
* this means that there's an ambiguity when several
* bindings are from the same host path:
*
* $ proot -m /tmp:/a -m /tmp:/b fchdir_getcwd /a
* /b
*
* $ proot -m /tmp:/b -m /tmp:/a fchdir_getcwd /a
* /a
*
* A solution would be to follow each file descriptor
* just like it is done for cwd.
*/
status = detranslate_path(tracee, path, NULL);
if (status < 0)
break;
/* Remove the trailing "/" or "/.". */
chop_finality(path);
tmp = talloc_strdup(tracee->fs, path);
if (tmp == NULL) {
status = -ENOMEM;
break;
}
TALLOC_FREE(tracee->fs->cwd);
tracee->fs->cwd = tmp;
talloc_set_name_const(tracee->fs->cwd, "$cwd");
set_sysnum(tracee, PR_void);
status = 0;
break;
}
case PR_bind:
case PR_connect: {
word_t address;
word_t size;
address = peek_reg(tracee, CURRENT, SYSARG_2);
size = peek_reg(tracee, CURRENT, SYSARG_3);
status = translate_socketcall_enter(tracee, &address, size);
if (status <= 0)
break;
poke_reg(tracee, SYSARG_2, address);
poke_reg(tracee, SYSARG_3, sizeof(struct sockaddr_un));
status = 0;
break;
}
#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee))
#define PEEK_WORD(addr, forced_errno) \
peek_word(tracee, addr); \
if (errno != 0) { \
status = forced_errno ?: -errno; \
break; \
}
#define POKE_WORD(addr, value) \
poke_word(tracee, addr, value); \
if (errno != 0) { \
status = -errno; \
break; \
}
case PR_accept:
case PR_accept4:
/* Nothing special to do if no sockaddr was specified. */
if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) {
status = 0;
break;
}
special = true;
/* Fall through. */
case PR_getsockname:
case PR_getpeername:{
int size;
/* Remember: PEEK_WORD puts -errno in status and breaks if an
* error occured. */
size = (int) PEEK_WORD(peek_reg(tracee, ORIGINAL, SYSARG_3), special ? -EINVAL : 0);
/* The "size" argument is both used as an input parameter
* (max. size) and as an output parameter (actual size). The
* exit stage needs to know the max. size to not overwrite
* anything, that's why it is copied in the 6th argument
* (unused) before the kernel updates it. */
poke_reg(tracee, SYSARG_6, size);
status = 0;
break;
}
#undef SYSARG_ADDR
#undef PEEK_WORD
#undef POKE_WORD
case PR_access:
case PR_chmod:
case PR_chown:
case PR_chown32:
case PR_getxattr:
case PR_listxattr:
case PR_mknod:
case PR_removexattr:
case PR_setxattr:
case PR_stat:
case PR_stat64:
case PR_statfs:
case PR_statfs64:
case PR_truncate:
case PR_truncate64:
case PR_utime:
case PR_utimes:
status = translate_sysarg(tracee, SYSARG_1, REGULAR);
break;
case PR_open:
flags = peek_reg(tracee, CURRENT, SYSARG_2);
if ( ((flags & O_NOFOLLOW) != 0)
|| ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0))
status = translate_sysarg(tracee, SYSARG_1, SYMLINK);
else
status = translate_sysarg(tracee, SYSARG_1, REGULAR);
break;
case PR_fchownat:
case PR_fstatat64:
case PR_newfstatat:
case PR_utimensat:
case PR_name_to_handle_at:
dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
status = get_sysarg_path(tracee, path, SYSARG_2);
if (status < 0)
break;
flags = ( syscall_number == PR_fchownat
|| syscall_number == PR_name_to_handle_at)
? peek_reg(tracee, CURRENT, SYSARG_5)
: peek_reg(tracee, CURRENT, SYSARG_4);
if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK);
else
status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR);
break;
case PR_fchmodat:
case PR_faccessat:
case PR_faccessat2:
case PR_futimesat:
case PR_mknodat:
dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
status = get_sysarg_path(tracee, path, SYSARG_2);
if (status < 0)
break;
status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR);
break;
case PR_inotify_add_watch:
flags = peek_reg(tracee, CURRENT, SYSARG_3);
if ((flags & IN_DONT_FOLLOW) != 0)
status = translate_sysarg(tracee, SYSARG_2, SYMLINK);
else
status = translate_sysarg(tracee, SYSARG_2, REGULAR);
break;
case PR_readlink:
case PR_lchown:
case PR_lchown32:
case PR_lgetxattr:
case PR_llistxattr:
case PR_lremovexattr:
case PR_lsetxattr:
case PR_lstat:
case PR_lstat64:
case PR_unlink:
case PR_rmdir:
case PR_mkdir:
status = translate_sysarg(tracee, SYSARG_1, SYMLINK);
break;
case PR_linkat:
olddirfd = peek_reg(tracee, CURRENT, SYSARG_1);
newdirfd = peek_reg(tracee, CURRENT, SYSARG_3);
flags = peek_reg(tracee, CURRENT, SYSARG_5);
status = get_sysarg_path(tracee, oldpath, SYSARG_2);
if (status < 0)
break;
status = get_sysarg_path(tracee, newpath, SYSARG_4);
if (status < 0)
break;
if ((flags & AT_SYMLINK_FOLLOW) != 0)
status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, REGULAR);
else
status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK);
if (status < 0)
break;
status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK);
break;
case PR_openat:
dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
flags = peek_reg(tracee, CURRENT, SYSARG_3);
status = get_sysarg_path(tracee, path, SYSARG_2);
if (status < 0)
break;
if ( ((flags & O_NOFOLLOW) != 0)
|| ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0))
status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK);
else
status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR);
break;
case PR_readlinkat:
case PR_unlinkat:
case PR_mkdirat:
dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
status = get_sysarg_path(tracee, path, SYSARG_2);
if (status < 0)
break;
status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK);
break;
case PR_link:
case PR_rename:
status = translate_sysarg(tracee, SYSARG_1, SYMLINK);
if (status < 0)
break;
status = translate_sysarg(tracee, SYSARG_2, SYMLINK);
break;
case PR_renameat:
case PR_renameat2:
olddirfd = peek_reg(tracee, CURRENT, SYSARG_1);
newdirfd = peek_reg(tracee, CURRENT, SYSARG_3);
status = get_sysarg_path(tracee, oldpath, SYSARG_2);
if (status < 0)
break;
status = get_sysarg_path(tracee, newpath, SYSARG_4);
if (status < 0)
break;
status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK);
if (status < 0)
break;
status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK);
break;
case PR_symlink:
status = translate_sysarg(tracee, SYSARG_2, SYMLINK);
break;
case PR_symlinkat:
newdirfd = peek_reg(tracee, CURRENT, SYSARG_2);
status = get_sysarg_path(tracee, newpath, SYSARG_3);
if (status < 0)
break;
status = translate_path2(tracee, newdirfd, newpath, SYSARG_3, SYMLINK);
break;
case PR_prctl:
/* Prevent tracees from setting dumpable flag.
* (Otherwise it could break tracee memory access) */
if (peek_reg(tracee, CURRENT, SYSARG_1) == PR_SET_DUMPABLE) {
set_sysnum(tracee, PR_void);
status = 0;
}
break;
}
return status;
}
+435
View File
@@ -0,0 +1,435 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <errno.h> /* errno(3), E* */
#include <sys/utsname.h> /* struct utsname, */
#include <linux/net.h> /* SYS_*, */
#include <string.h> /* strlen(3), */
#include "cli/note.h"
#include "syscall/syscall.h"
#include "syscall/sysnum.h"
#include "syscall/socket.h"
#include "syscall/chain.h"
#include "syscall/heap.h"
#include "syscall/rlimit.h"
#include "execve/execve.h"
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
#include "tracee/seccomp.h"
#include "path/path.h"
#include "ptrace/ptrace.h"
#include "ptrace/wait.h"
#include "arch.h"
/**
* Translate the output arguments of the current @tracee's syscall in
* the @tracee->pid process area. This function sets the result of
* this syscall to @tracee->status if an error occured previously
* during the translation, that is, if @tracee->status is less than 0.
*/
void translate_syscall_exit(Tracee *tracee)
{
word_t syscall_number;
word_t syscall_result;
int status = 0;
/* Set the tracee's errno if an error occured previously during
* the translation. */
if (tracee->status < 0) {
poke_reg(tracee, SYSARG_RESULT, (word_t) tracee->status);
return;
}
/* If proot changed syscall to PR_void during enter,
* keep syscall result set during entry. */
if (peek_reg(tracee, MODIFIED, SYSARG_NUM) == SYSCALL_AVOIDER &&
peek_reg(tracee, ORIGINAL, SYSARG_NUM) != SYSCALL_AVOIDER) {
poke_reg(tracee, SYSARG_RESULT, peek_reg(tracee, MODIFIED, SYSARG_RESULT));
}
/* Translate output arguments:
* - break: update the syscall result register with "status"
* - return: nothing else to do.
*/
syscall_number = get_sysnum(tracee, ORIGINAL);
syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
switch (syscall_number) {
case PR_brk:
translate_brk_exit(tracee);
return;
case PR_getcwd: {
char path[PATH_MAX];
size_t new_size;
size_t size;
word_t output;
size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2);
if (size == 0) {
status = -EINVAL;
break;
}
/* Ensure cwd still exists. */
status = translate_path(tracee, path, AT_FDCWD, ".", false);
if (status < 0)
break;
new_size = strlen(tracee->fs->cwd) + 1;
if (size < new_size) {
status = -ERANGE;
break;
}
/* Overwrite the path. */
output = peek_reg(tracee, ORIGINAL, SYSARG_1);
status = write_data(tracee, output, tracee->fs->cwd, new_size);
if (status < 0)
break;
/* The value of "status" is used to update the returned value
* in translate_syscall_exit(). */
status = new_size;
break;
}
case PR_accept:
case PR_accept4:
/* Nothing special to do if no sockaddr was specified. */
if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0)
return;
/* Fall through. */
case PR_getsockname:
case PR_getpeername: {
word_t sock_addr;
word_t size_addr;
word_t max_size;
/* Error reported by the kernel. */
if ((int) syscall_result < 0)
return;
sock_addr = peek_reg(tracee, ORIGINAL, SYSARG_2);
size_addr = peek_reg(tracee, MODIFIED, SYSARG_3);
max_size = peek_reg(tracee, MODIFIED, SYSARG_6);
status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size);
if (status < 0)
break;
/* Don't overwrite the syscall result. */
return;
}
#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee))
#define POKE_WORD(addr, value) \
poke_word(tracee, addr, value); \
if (errno != 0) { \
status = -errno; \
break; \
}
#define PEEK_WORD(addr) \
peek_word(tracee, addr); \
if (errno != 0) { \
status = -errno; \
break; \
}
#undef SYSARG_ADDR
#undef PEEK_WORD
#undef POKE_WORD
case PR_fchdir:
case PR_chdir:
/* These syscalls are fully emulated, see enter.c for details
* (like errors). */
status = 0;
break;
case PR_rename:
case PR_renameat: {
char old_path[PATH_MAX];
char new_path[PATH_MAX];
ssize_t old_length;
ssize_t new_length;
Comparison comparison;
Reg old_reg;
Reg new_reg;
char *tmp;
/* Error reported by the kernel. */
if ((int) syscall_result < 0)
return;
if (syscall_number == PR_rename) {
old_reg = SYSARG_1;
new_reg = SYSARG_2;
}
else {
old_reg = SYSARG_2;
new_reg = SYSARG_4;
}
/* Get the old path, then convert it to the same
* "point-of-view" as tracee->fs->cwd (guest). */
status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg));
if (status < 0)
break;
status = detranslate_path(tracee, old_path, NULL);
if (status < 0)
break;
old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path));
/* Nothing special to do if the moved path is not the
* current working directory. */
comparison = compare_paths(old_path, tracee->fs->cwd);
if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) {
status = 0;
break;
}
/* Get the new path, then convert it to the same
* "point-of-view" as tracee->fs->cwd (guest). */
status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg));
if (status < 0)
break;
status = detranslate_path(tracee, new_path, NULL);
if (status < 0)
break;
new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path));
/* Sanity check. */
if (strlen(tracee->fs->cwd) >= PATH_MAX) {
status = 0;
break;
}
strcpy(old_path, tracee->fs->cwd);
/* Update the virtual current working directory. */
substitute_path_prefix(old_path, old_length, new_path, new_length);
tmp = talloc_strdup(tracee->fs, old_path);
if (tmp == NULL) {
status = -ENOMEM;
break;
}
TALLOC_FREE(tracee->fs->cwd);
tracee->fs->cwd = tmp;
status = 0;
break;
}
case PR_readlink:
case PR_readlinkat: {
char referee[PATH_MAX];
char referer[PATH_MAX];
size_t old_size;
size_t new_size;
size_t max_size;
word_t input;
word_t output;
/* Error reported by the kernel. */
if ((int) syscall_result < 0)
return;
old_size = syscall_result;
if (syscall_number == PR_readlink) {
output = peek_reg(tracee, ORIGINAL, SYSARG_2);
max_size = peek_reg(tracee, ORIGINAL, SYSARG_3);
input = peek_reg(tracee, MODIFIED, SYSARG_1);
}
else {
output = peek_reg(tracee, ORIGINAL, SYSARG_3);
max_size = peek_reg(tracee, ORIGINAL, SYSARG_4);
input = peek_reg(tracee, MODIFIED, SYSARG_2);
}
if (max_size > PATH_MAX)
max_size = PATH_MAX;
if (max_size == 0) {
status = -EINVAL;
break;
}
/* The kernel does NOT put the NULL terminating byte for
* readlink(2). */
status = read_data(tracee, referee, output, old_size);
if (status < 0)
break;
referee[old_size] = '\0';
/* Not optimal but safe (path is fully translated). */
status = read_path(tracee, referer, input);
if (status < 0)
break;
if (status >= PATH_MAX) {
status = -ENAMETOOLONG;
break;
}
if (status == 1) {
/* Empty path was passed (""),
* indicating that path is pointed to by fd passed in first argument */
word_t dirfd = peek_reg(tracee, ORIGINAL, SYSARG_1);
if (syscall_number == PR_readlink || dirfd < 0) {
status = -EBADF;
break;
}
status = readlink_proc_pid_fd(tracee->pid, dirfd, referer);
if (status < 0)
break;
}
status = detranslate_path(tracee, referee, referer);
if (status < 0)
break;
/* The original path doesn't require any transformation, i.e
* it is a symetric binding. */
if (status == 0)
return;
/* Overwrite the path. Note: the output buffer might be
* initialized with zeros but it was updated with the kernel
* result, and then with the detranslated result. This later
* might be shorter than the former, so it's safier to add a
* NULL terminating byte when possible. This problem was
* exposed by IDA Demo 6.3. */
if ((size_t) status < max_size) {
new_size = status - 1;
status = write_data(tracee, output, referee, status);
}
else {
new_size = max_size;
status = write_data(tracee, output, referee, max_size);
}
if (status < 0)
break;
/* The value of "status" is used to update the returned value
* in translate_syscall_exit(). */
status = new_size;
break;
}
case PR_execve:
translate_execve_exit(tracee);
return;
case PR_ptrace:
status = translate_ptrace_exit(tracee);
break;
case PR_wait4:
case PR_waitpid:
if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT)
return;
status = translate_wait_exit(tracee);
break;
case PR_setrlimit:
case PR_prlimit64:
/* Error reported by the kernel. */
if ((int) syscall_result < 0)
return;
status = translate_setrlimit_exit(tracee, syscall_number == PR_prlimit64);
if (status < 0)
break;
/* Don't overwrite the syscall result. */
return;
case PR_utime:
if ((int) syscall_result == -ENOSYS)
{
fix_and_restart_enosys_syscall(tracee);
}
return;
case PR_statfs:
case PR_statfs64: {
/* Possibly fake that /dev/shm is living on tmpfs */
char devshm_path[PATH_MAX];
char statfs_path[PATH_MAX];
/* Only perform changes to result of successful syscall
* (that is, path was valid, it doesn't have to point
* to mount root) */
if (syscall_result != 0) {
return;
}
if (translate_path(tracee, devshm_path, AT_FDCWD, "/dev/shm", true) < 0) {
VERBOSE(tracee, 5, "/dev/shm is not mounted, not changing statfs() result");
return;
}
if (read_path(tracee, statfs_path, peek_reg(tracee, MODIFIED, SYSARG_1)) < 0) {
VERBOSE(tracee, 5, "statfs() exit couldn't read statfs_path");
return;
}
Comparison comparison = compare_paths(devshm_path, statfs_path);
if (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX) {
VERBOSE(tracee, 5, "Updating statfs() result to fake tmpfs /dev/shm");
/* Write TMPFS_MAGIC at beginning of statfs structure.
*
* (It's at beginning of structure regardless of syscall variant
* (statfs vs statfs64) and architecture bitness
* (on 64 bit this field is 8 bytes long, but as long as it's
* little endian, it will need only first 4 bytes to be modified,
* as next 4 bytes will always be 0))
* */
word_t stat_addr = peek_reg(tracee, ORIGINAL, syscall_number == PR_statfs64 ? SYSARG_3 : SYSARG_2);
int write_status = write_data(tracee, stat_addr, "\x94\x19\x02\x01", 4);
if (write_status < 0) {
VERBOSE(tracee, 5, "Updating statfs() result failed");
}
}
else {
VERBOSE(tracee, 5, "statfs() not for /dev/shm, not changing result");
}
return;
}
default:
return;
}
poke_reg(tracee, SYSARG_RESULT, (word_t) status);
}
+207
View File
@@ -0,0 +1,207 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/mman.h> /* PROT_*, MAP_*, */
#include <assert.h> /* assert(3), */
#include <string.h> /* strerror(3), */
#include <unistd.h> /* sysconf(3), */
#include <sys/param.h> /* MIN(), MAX(), */
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "syscall/sysnum.h"
#include "execve/execve.h"
#include "cli/note.h"
#include "compat.h"
/* The size of the heap can be zero, unlike the size of a memory
* mapping. As a consequence, the first page of the "heap" memory
* mapping is discarded in order to emulate an empty heap. */
static word_t heap_offset = 0;
/**
* Put @tracee's heap to a reliable location. By default the Linux
* kernel puts it near loader's BSS, but this default location is not
* reliable since the kernel might put another memory mapping right
* after it (ie. continuously). In this case, @tracee's heap can't
* grow anymore and some programs like Bash will abort. This issue
* can be reproduced when using a Ubuntu 12.04 x86_64 rootfs on RHEL 5
* x86_64.
*/
void translate_brk_enter(Tracee *tracee)
{
word_t new_brk_address;
size_t old_heap_size;
size_t new_heap_size;
if (tracee->heap->disabled)
return;
if (heap_offset == 0) {
heap_offset = sysconf(_SC_PAGE_SIZE);
if ((int) heap_offset <= 0)
heap_offset = 0x1000;
}
new_brk_address = peek_reg(tracee, CURRENT, SYSARG_1);
/* Allocate a new mapping for the emulated heap. */
if (tracee->heap->base == 0) {
Sysnum sysnum;
Mapping *mappings;
Mapping *bss;
/* From PRoot's point-of-view this is the first time this
* tracee calls brk(2), although an address was specified.
* This is not supposed to happen the first time. It is
* likely because this tracee is the very first child of PRoot
* but the first execve(2) didn't happen yet (so this is not
* its first call to brk(2)). For instance, the installation
* of seccomp filters is made after this very first process is
* traced, and might call malloc(3) before the first
* execve(2). */
if (new_brk_address != 0) {
if (tracee->verbose > 0)
note(tracee, WARNING, INTERNAL,
"process %d is doing suspicious brk()", tracee->pid);
return;
}
/* Put the heap as close to the BSS as possible since
* some programs -- like dump-emacs -- assume the gap
* between the end of the BSS and the start of the
* heap is relatively small (ie. < 1MB) even if ALSR
* is enabled. Note that bss->addr + bss->length is
* naturally aligned to a page boundary according to
* add_mapping() in execve/enter.c, ie. no need to
* align new_brk_address again. Now, the gap between
* the BSS and the heap is only "heap_offset" bytes
* long. To emulate ADDR_NO_RANDOMIZE personality,
* this gap should be removed (not yet supported). */
mappings = tracee->load_info->mappings;
bss = &mappings[talloc_array_length(mappings) - 1];
new_brk_address = bss->addr + bss->length;
#ifdef ARCH_ARM64
sysnum = tracee->is_aarch32 ? PR_mmap2 : PR_mmap;
#else
sysnum = PR_mmap2;
#endif
set_sysnum(tracee, sysnum);
poke_reg(tracee, SYSARG_1 /* address */, new_brk_address);
poke_reg(tracee, SYSARG_2 /* length */, heap_offset);
poke_reg(tracee, SYSARG_3 /* prot */, PROT_READ | PROT_WRITE);
poke_reg(tracee, SYSARG_4 /* flags */, MAP_PRIVATE | MAP_ANONYMOUS);
poke_reg(tracee, SYSARG_5 /* fd */, -1);
poke_reg(tracee, SYSARG_6 /* offset */, 0);
return;
}
/* The size of the heap can't be negative. */
if (new_brk_address < tracee->heap->base) {
set_sysnum(tracee, PR_void);
return;
}
new_heap_size = new_brk_address - tracee->heap->base;
old_heap_size = tracee->heap->size;
/* Actually resizing. */
set_sysnum(tracee, PR_mremap);
poke_reg(tracee, SYSARG_1 /* old_address */, tracee->heap->base - heap_offset);
poke_reg(tracee, SYSARG_2 /* old_size */, old_heap_size + heap_offset);
poke_reg(tracee, SYSARG_3 /* new_size */, new_heap_size + heap_offset);
poke_reg(tracee, SYSARG_4 /* flags */, 0);
poke_reg(tracee, SYSARG_5 /* new_address */, 0);
return;
}
/**
* c.f. function above.
*/
void translate_brk_exit(Tracee *tracee)
{
word_t result;
word_t sysnum;
int tracee_errno;
if (tracee->heap->disabled)
return;
assert(heap_offset > 0);
sysnum = get_sysnum(tracee, MODIFIED);
result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
tracee_errno = (int) result;
switch (sysnum) {
case PR_void:
poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size);
break;
case PR_mmap:
case PR_mmap2:
/* On error, mmap(2) returns -errno (the last 4k is
* reserved for this), whereas brk(2) returns the
* previous value. */
if (tracee_errno < 0 && tracee_errno > -4096) {
poke_reg(tracee, SYSARG_RESULT, 0);
break;
}
tracee->heap->base = result + heap_offset;
tracee->heap->size = 0;
poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size);
break;
case PR_mremap:
/* On error, mremap(2) returns -errno (the last 4k is
* reserved this), whereas brk(2) returns the previous
* value. */
if ( (tracee_errno < 0 && tracee_errno > -4096)
|| (tracee->heap->base != result + heap_offset)) {
poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size);
break;
}
tracee->heap->size = peek_reg(tracee, MODIFIED, SYSARG_3) - heap_offset;
poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size);
break;
case PR_brk:
/* Is it confirmed that this suspicious call to brk(2)
* is actually legit? */
if (result == peek_reg(tracee, ORIGINAL, SYSARG_1))
tracee->heap->disabled = true;
break;
default:
assert(0);
}
}
+31
View File
@@ -0,0 +1,31 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef HEAP_H
#define HEAP_H
#include "tracee/tracee.h"
extern void translate_brk_enter(Tracee *tracee);
extern void translate_brk_exit(Tracee *tracee);
#endif /* HEAP_H */
+117
View File
@@ -0,0 +1,117 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stdbool.h> /* bool, */
#include <sys/time.h> /* prlimit(2), */
#include <sys/resource.h> /* prlimit(2), */
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
#include "cli/note.h"
/**
* Set PRoot's stack soft limit to @tracee's one if this latter is
* greater. This allows to workaround a Linux kernel bug that
* prevents a tracer to access a tracee's stack beyond its last mapped
* page, as it might by the case under PRoot. This function returns
* -errno if an error occurred, otherwise 0.
*
* Details: when a tracer tries to access a tracee's stack beyond its
* last mapped page, the Linux kernel should be able to increase
* tracee's stack up to its soft limit. Unfortunately the Linux
* kernel checks the limit of the tracer instead the limit of the
* tracee. This bug was exposed using UMEQ under PRoot.
*
* Ref.: https://bugzilla.kernel.org/show_bug.cgi?id=91791
*
* Three strategies were possible:
*
* - set PRoot's stack soft limit to the hard limit; this might make
* the system collapse if PRoot starts to recurses indefinitely.
*
* - as it's done here; this appears to be a good compromise between
* the strategy above and the one below.
*
* - as it's done here + reduce PRoot's stack soft limit as soon as
* it's possible; this would be overly complicated.
*/
int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit)
{
struct rlimit64 proot_stack;
word_t resource;
word_t address;
word_t tracee_stack_limit;
Reg sysarg;
int status;
sysarg = (is_prlimit ? SYSARG_2 : SYSARG_1);
resource = peek_reg(tracee, ORIGINAL, sysarg);
address = peek_reg(tracee, ORIGINAL, sysarg + 1);
/* Not the resource we're looking for? */
if (resource != RLIMIT_STACK)
return 0;
/* Retrieve new tracee's stack limit. */
if (is_prlimit) {
/* Not the prlimit usage we're looking for? */
if (address == 0)
return 0;
tracee_stack_limit = peek_uint64(tracee, address);
}
else {
tracee_stack_limit = peek_word(tracee, address);
/* Convert this special value from 32-bit to 64-bit,
* if needed. */
if (is_32on64_mode(tracee) && tracee_stack_limit == (uint32_t) -1)
tracee_stack_limit = RLIM_INFINITY;
}
if (errno != 0)
return -errno;
/* Get current PRoot's stack limit. */
status = prlimit64(0, RLIMIT_STACK, NULL, &proot_stack);
if (status < 0) {
VERBOSE(tracee, 1, "can't get stack limit.");
return 0; /* Not fatal. */
}
/* No need to increase current PRoot's stack limit? */
if (proot_stack.rlim_cur >= tracee_stack_limit)
return 0;
proot_stack.rlim_cur = tracee_stack_limit;
/* Increase current PRoot's stack limit. */
status = prlimit64(0, RLIMIT_STACK, &proot_stack, NULL);
if (status < 0)
VERBOSE(tracee, 1, "can't set stack limit.");
return 0; /* Not fatal. */
VERBOSE(tracee, 1, "stack soft limit increased to %llu bytes", proot_stack.rlim_cur);
return 0;
}
@@ -0,0 +1,31 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef RLIMIT_H
#define RLIMIT_H
#include <stdbool.h>
#include "tracee/tracee.h"
extern int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit);
#endif /* RLIMIT_H */
@@ -0,0 +1,471 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include "arch.h"
#include <sys/prctl.h> /* prctl(2), PR_* */
#include <linux/filter.h> /* struct sock_*, */
#include <linux/seccomp.h> /* SECCOMP_MODE_FILTER, */
#include <linux/filter.h> /* struct sock_*, */
#include <linux/audit.h> /* AUDIT_, */
#include <sys/queue.h> /* LIST_FOREACH, */
#include <sys/types.h> /* size_t, */
#include <talloc.h> /* talloc_*, */
#include <errno.h> /* E*, */
#include <string.h> /* memcpy(3), */
#include <stddef.h> /* offsetof(3), */
#include <stdint.h> /* uint*_t, UINT*_MAX, */
#include <assert.h> /* assert(3), */
#include "syscall/seccomp.h"
#include "tracee/tracee.h"
#include "syscall/syscall.h"
#include "syscall/sysnum.h"
#include "cli/note.h"
#include "compat.h"
#include "attribute.h"
#define DEBUG_FILTER(...) /* fprintf(stderr, __VA_ARGS__) */
/**
* Allocate an empty @program->filter. This function returns -errno
* if an error occurred, otherwise 0.
*/
static int new_program_filter(struct sock_fprog *program)
{
program->filter = talloc_array(NULL, struct sock_filter, 0);
if (program->filter == NULL)
return -ENOMEM;
program->len = 0;
return 0;
}
/**
* Append to @program->filter the given @statements (@nb_statements
* items). This function returns -errno if an error occurred,
* otherwise 0.
*/
static int add_statements(struct sock_fprog *program, size_t nb_statements,
struct sock_filter *statements)
{
size_t length;
void *tmp;
size_t i;
length = talloc_array_length(program->filter);
tmp = talloc_realloc(NULL, program->filter, struct sock_filter, length + nb_statements);
if (tmp == NULL)
return -ENOMEM;
program->filter = tmp;
for (i = 0; i < nb_statements; i++, length++)
memcpy(&program->filter[length], &statements[i], sizeof(struct sock_filter));
return 0;
}
/**
* Append to @program->filter the statements required to notify PRoot
* about the given @syscall made by a tracee, with the given @flag.
* This function returns -errno if an error occurred, otherwise 0.
*/
static int add_trace_syscall(struct sock_fprog *program, word_t syscall, int flag)
{
int status;
/* Sanity check. */
if (syscall > UINT32_MAX)
return -ERANGE;
#define LENGTH_TRACE_SYSCALL 2
struct sock_filter statements[LENGTH_TRACE_SYSCALL] = {
/* Compare the accumulator with the expected syscall:
* skip the next statement if not equal. */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, syscall, 0, 1),
/* Notify the tracer. */
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE + flag)
};
DEBUG_FILTER("FILTER: trace if syscall == %ld\n", syscall);
status = add_statements(program, LENGTH_TRACE_SYSCALL, statements);
if (status < 0)
return status;
return 0;
}
/**
* Append to @program->filter the statements that allow anything (if
* unfiltered). Note that @nb_traced_syscalls is used to make a
* sanity check. This function returns -errno if an error occurred,
* otherwise 0.
*/
static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscalls)
{
int status;
#define LENGTH_END_SECTION 1
struct sock_filter statements[LENGTH_END_SECTION] = {
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
};
DEBUG_FILTER("FILTER: allow\n");
status = add_statements(program, LENGTH_END_SECTION, statements);
if (status < 0)
return status;
/* Sanity check, see start_arch_section(). */
if ( talloc_array_length(program->filter) - program->len
!= LENGTH_END_SECTION + nb_traced_syscalls * LENGTH_TRACE_SYSCALL)
return -ERANGE;
return 0;
}
/**
* Append to @program->filter the statements that check the current
* @architecture. Note that @nb_traced_syscalls is used to make a
* sanity check. This function returns -errno if an error occurred,
* otherwise 0.
*/
static int start_arch_section(struct sock_fprog *program, uint32_t arch, size_t nb_traced_syscalls)
{
const size_t arch_offset = offsetof(struct seccomp_data, arch);
const size_t syscall_offset = offsetof(struct seccomp_data, nr);
const size_t section_length = LENGTH_END_SECTION +
nb_traced_syscalls * LENGTH_TRACE_SYSCALL;
int status;
/* Sanity checks. */
if ( arch_offset > UINT32_MAX
|| syscall_offset > UINT32_MAX
|| section_length > UINT32_MAX - 1)
return -ERANGE;
#define LENGTH_START_SECTION 4
struct sock_filter statements[LENGTH_START_SECTION] = {
/* Load the current architecture into the
* accumulator. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_offset),
/* Compare the accumulator with the expected
* architecture: skip the following statement if
* equal. */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 1, 0),
/* This is not the expected architecture, so jump
* unconditionally to the end of this section. */
BPF_STMT(BPF_JMP + BPF_JA + BPF_K, section_length + 1),
/* This is the expected architecture, so load the
* current syscall into the accumulator. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_offset)
};
DEBUG_FILTER("FILTER: if arch == %ld, up to %zdth statement\n",
arch, nb_traced_syscalls);
status = add_statements(program, LENGTH_START_SECTION, statements);
if (status < 0)
return status;
/* See the sanity check in end_arch_section(). */
program->len = talloc_array_length(program->filter);
return 0;
}
/**
* Append to @program->filter the statements that forbid anything (if
* unfiltered) and update @program->len. This function returns -errno
* if an error occurred, otherwise 0.
*/
static int finalize_program_filter(struct sock_fprog *program)
{
int status;
#define LENGTH_FINALIZE 1
struct sock_filter statements[LENGTH_FINALIZE] = {
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
};
DEBUG_FILTER("FILTER: kill\n");
status = add_statements(program, LENGTH_FINALIZE, statements);
if (status < 0)
return status;
program->len = talloc_array_length(program->filter);
return 0;
}
/**
* Free @program->filter and set @program->len to 0.
*/
static void free_program_filter(struct sock_fprog *program)
{
TALLOC_FREE(program->filter);
program->len = 0;
}
/**
* Convert the given @sysnums into BPF filters according to the
* following pseudo-code, then enabled them for the given @tracee and
* all of its future children:
*
* for each handled architectures
* for each filtered syscall
* trace
* allow
* kill
*
* This function returns -errno if an error occurred, otherwise 0.
*/
static int set_seccomp_filters(const FilteredSysnum *sysnums)
{
SeccompArch seccomp_archs[] = SECCOMP_ARCHS;
size_t nb_archs = sizeof(seccomp_archs) / sizeof(SeccompArch);
struct sock_fprog program = { .len = 0, .filter = NULL };
size_t nb_traced_syscalls;
size_t i, j, k;
int status;
status = new_program_filter(&program);
if (status < 0)
goto end;
/* For each handled architectures */
for (i = 0; i < nb_archs; i++) {
word_t syscall;
nb_traced_syscalls = 0;
/* Pre-compute the number of traced syscalls for this architecture. */
for (j = 0; j < seccomp_archs[i].nb_abis; j++) {
for (k = 0; sysnums[k].value != PR_void; k++) {
syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value);
if (syscall != SYSCALL_AVOIDER)
nb_traced_syscalls++;
}
}
/* Filter: if handled architecture */
status = start_arch_section(&program, seccomp_archs[i].value, nb_traced_syscalls);
if (status < 0)
goto end;
for (j = 0; j < seccomp_archs[i].nb_abis; j++) {
for (k = 0; sysnums[k].value != PR_void; k++) {
/* Get the architecture specific syscall number. */
syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value);
if (syscall == SYSCALL_AVOIDER)
continue;
/* Filter: trace if handled syscall */
status = add_trace_syscall(&program, syscall, sysnums[k].flags);
if (status < 0)
goto end;
}
}
/* Filter: allow untraced syscalls for this architecture */
status = end_arch_section(&program, nb_traced_syscalls);
if (status < 0)
goto end;
}
status = finalize_program_filter(&program);
if (status < 0)
goto end;
status = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
if (status < 0)
goto end;
/* To output this BPF program for debug purpose:
*
* write(2, program.filter, program.len * sizeof(struct sock_filter));
*/
status = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program);
if (status < 0)
goto end;
status = 0;
end:
free_program_filter(&program);
return status;
}
/* List of sysnums handled by PRoot. */
static FilteredSysnum proot_sysnums[] = {
{ PR_accept, FILTER_SYSEXIT },
{ PR_accept4, FILTER_SYSEXIT },
{ PR_access, 0 },
{ PR_bind, 0 },
{ PR_brk, FILTER_SYSEXIT },
{ PR_chdir, FILTER_SYSEXIT },
{ PR_chmod, 0 },
{ PR_chown, 0 },
{ PR_chown32, 0 },
{ PR_connect, 0 },
{ PR_execve, FILTER_SYSEXIT },
{ PR_faccessat, 0 },
{ PR_faccessat2, 0 },
{ PR_fchdir, FILTER_SYSEXIT },
{ PR_fchmodat, 0 },
{ PR_fchownat, 0 },
{ PR_fstatat64, 0 },
{ PR_futimesat, 0 },
{ PR_getcwd, FILTER_SYSEXIT },
{ PR_getpeername, FILTER_SYSEXIT },
{ PR_getsockname, FILTER_SYSEXIT },
{ PR_getxattr, 0 },
{ PR_inotify_add_watch, 0 },
{ PR_lchown, 0 },
{ PR_lchown32, 0 },
{ PR_lgetxattr, 0 },
{ PR_link, 0 },
{ PR_linkat, 0 },
{ PR_listxattr, 0 },
{ PR_llistxattr, 0 },
{ PR_lremovexattr, 0 },
{ PR_lsetxattr, 0 },
{ PR_lstat, 0 },
{ PR_lstat64, 0 },
{ PR_mkdir, 0 },
{ PR_mkdirat, 0 },
{ PR_mknod, 0 },
{ PR_mknodat, 0 },
{ PR_name_to_handle_at, 0 },
{ PR_newfstatat, 0 },
{ PR_open, 0 },
{ PR_openat, 0 },
{ PR_prctl, 0 },
{ PR_prlimit64, FILTER_SYSEXIT },
{ PR_ptrace, FILTER_SYSEXIT },
{ PR_readlink, FILTER_SYSEXIT },
{ PR_readlinkat, FILTER_SYSEXIT },
{ PR_removexattr, 0 },
{ PR_rename, FILTER_SYSEXIT },
{ PR_renameat, FILTER_SYSEXIT },
{ PR_renameat2, FILTER_SYSEXIT },
{ PR_rmdir, 0 },
{ PR_setrlimit, FILTER_SYSEXIT },
{ PR_setxattr, 0 },
{ PR_stat, 0 },
{ PR_stat64, 0 },
{ PR_statfs, FILTER_SYSEXIT },
{ PR_statfs64, FILTER_SYSEXIT },
{ PR_symlink, 0 },
{ PR_symlinkat, 0 },
{ PR_truncate, 0 },
{ PR_truncate64, 0 },
{ PR_uname, FILTER_SYSEXIT },
{ PR_unlink, 0 },
{ PR_unlinkat, 0 },
{ PR_utime, FILTER_SYSEXIT },
{ PR_utimensat, 0 },
{ PR_utimes, 0 },
{ PR_wait4, FILTER_SYSEXIT },
{ PR_waitpid, FILTER_SYSEXIT },
FILTERED_SYSNUM_END,
};
/**
* Add the @new_sysnums to the list of filtered @sysnums, using the
* given Talloc @context. This function returns -errno if an error
* occurred, otherwise 0.
*/
static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums,
const FilteredSysnum *new_sysnums)
{
size_t i, j;
assert(sysnums != NULL);
if (*sysnums == NULL) {
/* Start with no sysnums but the terminator. */
*sysnums = talloc_array(context, FilteredSysnum, 1);
if (*sysnums == NULL)
return -ENOMEM;
(*sysnums)[0].value = PR_void;
}
for (i = 0; new_sysnums[i].value != PR_void; i++) {
/* Search for the given sysnum. */
for (j = 0; (*sysnums)[j].value != PR_void
&& (*sysnums)[j].value != new_sysnums[i].value; j++)
;
if ((*sysnums)[j].value == PR_void) {
/* No such sysnum, allocate a new entry. */
(*sysnums) = talloc_realloc(context, (*sysnums), FilteredSysnum, j + 2);
if ((*sysnums) == NULL)
return -ENOMEM;
(*sysnums)[j] = new_sysnums[i];
/* The last item is the terminator. */
(*sysnums)[j + 1].value = PR_void;
}
else
/* The sysnum is already filtered, merge the
* flags. */
(*sysnums)[j].flags |= new_sysnums[i].flags;
}
return 0;
}
/**
* Tell the kernel to trace only syscalls handled by PRoot.
* This filter will be enabled for the given @tracee and
* all of its future children. This function returns -errno if an
* error occurred, otherwise 0.
*/
int enable_syscall_filtering(const Tracee *tracee)
{
FilteredSysnum *filtered_sysnums = NULL;
int status;
assert(tracee != NULL && tracee->ctx != NULL);
/* Add the sysnums required by PRoot to the list of filtered sysnums. */
status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums);
if (status < 0)
return status;
status = set_seccomp_filters(filtered_sysnums);
if (status < 0)
return status;
return 0;
}
@@ -0,0 +1,48 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SECCOMP_H
#define SECCOMP_H
#include "syscall/sysnum.h"
#include "tracee/tracee.h"
#include "attribute.h"
#include "arch.h"
typedef struct {
Sysnum value;
word_t flags;
} FilteredSysnum;
typedef struct {
unsigned int value;
size_t nb_abis;
Abi abis[NB_MAX_ABIS];
} SeccompArch;
#define FILTERED_SYSNUM_END { PR_void, 0 }
#define FILTER_SYSEXIT 0x1
extern int enable_syscall_filtering(const Tracee *tracee);
#endif /* SECCOMP_H */
+216
View File
@@ -0,0 +1,216 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stddef.h> /* offsetof(3), */
#include <strings.h> /* bzero(3), */
#include <string.h> /* strncpy(3), strlen(3), */
#include <assert.h> /* assert(3), */
#include <errno.h> /* E*, */
#include <sys/socket.h> /* struct sockaddr_un, AF_UNIX, */
#include <sys/un.h> /* struct sockaddr_un, */
#include <sys/param.h> /* MIN(), MAX(), */
#include "syscall/socket.h"
#include "tracee/tracee.h"
#include "tracee/mem.h"
#include "path/binding.h"
#include "path/temp.h"
#include "path/path.h"
#include "arch.h"
#include "compat.h"
/* The sockaddr_un structure has exactly the same layout on all
* architectures. */
static const off_t offsetof_path = offsetof(struct sockaddr_un, sun_path);
extern struct sockaddr_un sockaddr_un__;
static const size_t sizeof_path = sizeof(sockaddr_un__.sun_path);
/**
* Copy in @sockaddr the struct sockaddr_un stored in the @tracee
* memory at the given @address. Also, its pathname is copied to the
* null-terminated @path. Only @size bytes are read from the @tracee
* memory (should be <= @max_size <= sizeof(struct sockaddr_un)).
* This function returns -errno if an error occurred, 0 if the
* structure was not found (not a sockaddr_un or @size > @max_size),
* otherwise 1.
*/
static int read_sockaddr_un(Tracee *tracee, struct sockaddr_un *sockaddr, word_t max_size,
char path[PATH_MAX], word_t address, int size)
{
int status;
assert(max_size <= sizeof(struct sockaddr_un));
/* Nothing to do if the sockaddr has an unexpected size. */
if (size <= offsetof_path || (word_t) size > max_size)
return 0;
bzero(sockaddr, sizeof(struct sockaddr_un));
status = read_data(tracee, sockaddr, address, size);
if (status < 0)
return status;
/* Nothing to do if it's not a named Unix domain socket. */
if ((sockaddr->sun_family != AF_UNIX)
|| sockaddr->sun_path[0] == '\0')
return 0;
/* Be careful: sun_path doesn't have to be null-terminated. */
assert(sizeof_path < PATH_MAX - 1);
strncpy(path, sockaddr->sun_path, sizeof_path);
path[sizeof_path] = '\0';
return 1;
}
/**
* Translate the pathname of the struct sockaddr_un currently stored
* in the @tracee memory at the given @address. See the documentation
* of read_sockaddr_un() for the meaning of the @size parameter.
* Also, the new address of the translated sockaddr_un is put in the
* @address parameter. This function returns -errno if an error
* occurred, otherwise 0.
*/
int translate_socketcall_enter(Tracee *tracee, word_t *address, int size)
{
struct sockaddr_un sockaddr;
char user_path[PATH_MAX];
char host_path[PATH_MAX];
int status;
if (*address == 0)
return 0;
status = read_sockaddr_un(tracee, &sockaddr, sizeof(sockaddr), user_path, *address, size);
if (status <= 0)
return status;
status = translate_path(tracee, host_path, AT_FDCWD, user_path, true);
if (status < 0)
return status;
/* Be careful: sun_path doesn't have to be null-terminated. */
if (strlen(host_path) > sizeof_path) {
char *shorter_host_path;
Binding *binding;
/* The translated path is too long to fit the sun_path
* array, so let's bind it to a shorter path. */
shorter_host_path = create_temp_name(tracee->ctx, "proot");
if (shorter_host_path == NULL || strlen(shorter_host_path) > sizeof_path)
return -EINVAL;
(void) mkstemp(shorter_host_path);
if (strlen(shorter_host_path) > sizeof_path)
return -EINVAL;
/* Ensure the guest path of this new binding is
* canonicalized, as it is always assumed. */
strcpy(user_path, host_path);
status = detranslate_path(tracee, user_path, NULL);
if (status < 0)
return -EINVAL;
/* Bing the guest path to a shorter host path. */
binding = insort_binding3(tracee, tracee->ctx, shorter_host_path, user_path);
if (binding == NULL)
return -EINVAL;
/* This temporary file (shorter_host_path) will be removed once the
* binding is destroyed. */
talloc_reparent(tracee->ctx, binding, shorter_host_path);
/* Let's use this shorter path now. */
strcpy(host_path, shorter_host_path);
}
strncpy(sockaddr.sun_path, host_path, sizeof_path);
/* Push the updated sockaddr to a newly allocated space. */
*address = alloc_mem(tracee, sizeof(sockaddr));
if (*address == 0)
return -EFAULT;
status = write_data(tracee, *address, &sockaddr, sizeof(sockaddr));
if (status < 0)
return status;
return 1;
}
/**
* Detranslate the pathname of the struct sockaddr_un currently stored
* in the @tracee memory at the given @sock_addr. See the
* documentation of read_sockaddr_un() for the meaning of the
* @size_addr and @max_size parameters. This function returns -errno
* if an error occurred, otherwise 0.
*/
int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size)
{
struct sockaddr_un sockaddr;
bool is_truncated = false;
char path[PATH_MAX];
int status;
int size;
if (sock_addr == 0)
return 0;
size = peek_int32(tracee, size_addr);
if (errno != 0)
return -errno;
max_size = MIN(max_size, sizeof(sockaddr));
status = read_sockaddr_un(tracee, &sockaddr, max_size, path, sock_addr, size);
if (status <= 0)
return status;
status = detranslate_path(tracee, path, NULL);
if (status < 0)
return status;
/* Be careful: sun_path doesn't have to be null-terminated. */
size = offsetof_path + strlen(path) + 1;
if (size < 0 || (word_t) size > max_size) {
size = max_size;
is_truncated = true;
}
strncpy(sockaddr.sun_path, path, sizeof_path);
/* Overwrite the sockaddr and socklen parameters. */
status = write_data(tracee, sock_addr, &sockaddr, size);
if (status < 0)
return status;
/* If sockaddr is truncated (because the buffer provided is
* too small), addrlen will return a value greater than was
* supplied to the call. See man 2 accept. */
if (is_truncated)
size = max_size + 1;
poke_int32(tracee, size_addr, size);
if (errno != 0)
return -errno;
return 0;
}
@@ -0,0 +1,32 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SOCKET_H
#define SOCKET_H
#include "arch.h" /* word_t */
#include "tracee/tracee.h"
int translate_socketcall_enter(Tracee *tracee, word_t *sock_addr, int size);
int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size);
#endif /* SOCKET_H */
@@ -0,0 +1,233 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <assert.h> /* assert(3), */
#include <limits.h> /* PATH_MAX, */
#include <string.h> /* strlen(3), */
#include <errno.h> /* errno(3), E* */
#include "syscall/syscall.h"
#include "syscall/chain.h"
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/mem.h"
#include "cli/note.h"
/**
* Copy in @path a C string (PATH_MAX bytes max.) from the @tracee's
* memory address space pointed to by the @reg argument of the
* current syscall. This function returns -errno if an error occured,
* otherwise it returns the size in bytes put into the @path.
*/
int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg)
{
int size;
word_t src;
src = peek_reg(tracee, CURRENT, reg);
/* Check if the parameter is not NULL. Technically we should
* not return an -EFAULT for this special value since it is
* allowed for some syscall, utimensat(2) for instance. */
if (src == 0) {
path[0] = '\0';
return 0;
}
/* Get the path from the tracee's memory space. */
size = read_path(tracee, path, src);
if (size < 0)
return size;
path[size] = '\0';
return size;
}
/**
* Copy @size bytes of the data pointed to by @tracer_ptr into a
* @tracee's memory block and make the @reg argument of the current
* syscall points to this new block. This function returns -errno if
* an error occured, otherwise 0.
*/
int set_sysarg_data(Tracee *tracee, const void *tracer_ptr, word_t size, Reg reg)
{
word_t tracee_ptr;
int status;
/* Allocate space into the tracee's memory to host the new data. */
tracee_ptr = alloc_mem(tracee, size);
if (tracee_ptr == 0)
return -EFAULT;
/* Copy the new data into the previously allocated space. */
status = write_data(tracee, tracee_ptr, tracer_ptr, size);
if (status < 0)
return status;
/* Make this argument point to the new data. */
poke_reg(tracee, reg, tracee_ptr);
return 0;
}
/**
* Copy @path to a @tracee's memory block and make the @reg argument
* of the current syscall points to this new block. This function
* returns -errno if an error occured, otherwise 0.
*/
int set_sysarg_path(Tracee *tracee, const char path[PATH_MAX], Reg reg)
{
return set_sysarg_data(tracee, path, strlen(path) + 1, reg);
}
void translate_syscall(Tracee *tracee)
{
const bool is_enter_stage = IS_IN_SYSENTER(tracee);
int status;
assert(tracee->exe != NULL);
status = fetch_regs(tracee);
if (status < 0)
return;
int suppressed_syscall_status = 0;
if (is_enter_stage) {
/* Never restore original register values at the end
* of this stage. */
tracee->restore_original_regs = false;
print_current_regs(tracee, 3, "sysenter start");
/* Translate the syscall only if it was actually
* requested by the tracee, it is not a syscall
* chained by PRoot. */
if (tracee->chain.syscalls == NULL) {
save_current_regs(tracee, ORIGINAL);
status = translate_syscall_enter(tracee);
save_current_regs(tracee, MODIFIED);
}
else {
if (tracee->chain.sysnum_workaround_state != SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL)
status = 0;
tracee->restart_how = PTRACE_SYSCALL;
}
/* Remember the tracee status for the "exit" stage and
* avoid the actual syscall if an error was reported
* by the translation. */
if (status < 0) {
set_sysnum(tracee, PR_void);
poke_reg(tracee, SYSARG_RESULT, (word_t) status);
tracee->status = status;
#if defined(ARCH_ARM_EABI)
tracee->restart_how = PTRACE_SYSCALL;
#endif
}
else
tracee->status = 1;
/* Restore tracee's stack pointer now if it won't hit
* the sysexit stage (i.e. when seccomp is enabled and
* there's nothing else to do). */
if (tracee->restart_how == PTRACE_CONT) {
suppressed_syscall_status = tracee->status;
tracee->status = 0;
poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, STACK_POINTER));
}
}
else {
/* By default, restore original register values at the
* end of this stage. */
tracee->restore_original_regs = true;
print_current_regs(tracee, 5, "sysexit start");
/* Translate the syscall only if it was actually
* requested by the tracee, it is not a syscall
* chained by PRoot. */
if (tracee->chain.syscalls == NULL || tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) {
tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_INACTIVE;
translate_syscall_exit(tracee);
}
else if (tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL) {
tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL;
}
/* Reset the tracee's status. */
tracee->status = 0;
/* Insert the next chained syscall, if any. */
if (tracee->chain.syscalls != NULL)
chain_next_syscall(tracee);
}
bool override_sysnum = is_enter_stage && tracee->chain.syscalls == NULL;
int push_regs_status = push_specific_regs(tracee, override_sysnum);
/* Handle inability to change syscall number */
if (push_regs_status < 0 && override_sysnum) {
word_t orig_sysnum = peek_reg(tracee, ORIGINAL, SYSARG_NUM);
word_t current_sysnum = peek_reg(tracee, CURRENT, SYSARG_NUM);
print_current_regs(tracee, 4, "pre_push");
if (orig_sysnum != current_sysnum) {
/* Restart current syscall as chained */
if (current_sysnum != SYSCALL_AVOIDER) {
restart_current_syscall_as_chained(tracee);
} else if (suppressed_syscall_status) {
/* If we've decided to fail this syscall
* by setting it to no-op and continuing, but turns out
* that we can't just make syscall nop, restore tracee->status
* and intercept syscall exit */
tracee->status = suppressed_syscall_status;
tracee->restart_how = PTRACE_SYSCALL;
}
/* Set syscall arguments to make it fail
* For most syscalls we set all args to -1
* Hoping there is among them invalid request/address/fd/value that will make syscall fail */
poke_reg(tracee, SYSARG_1, -1);
poke_reg(tracee, SYSARG_2, -1);
poke_reg(tracee, SYSARG_3, -1);
poke_reg(tracee, SYSARG_4, -1);
poke_reg(tracee, SYSARG_5, -1);
poke_reg(tracee, SYSARG_6, -1);
if (get_sysnum(tracee, ORIGINAL) == PR_brk) {
/* For brk() we pass 0 as first arg; this is used to query value without changing it */
poke_reg(tracee, SYSARG_1, 0);
}
/* Push regs again without changing syscall */
push_regs_status = push_specific_regs(tracee, false);
if (push_regs_status != 0) {
note(tracee, WARNING, SYSTEM, "can't set tracee registers in workaround");
}
}
}
if (is_enter_stage)
print_current_regs(tracee, 5, "sysenter end" );
else
print_current_regs(tracee, 4, "sysexit end");
}
@@ -0,0 +1,39 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SYSCALL_H
#define SYSCALL_H
#include <limits.h> /* PATH_MAX, */
#include "tracee/tracee.h"
#include "tracee/reg.h"
extern int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg);
extern int set_sysarg_path(Tracee *tracee, const char path[PATH_MAX], Reg reg);
extern int set_sysarg_data(Tracee *tracee, const void *tracer_ptr, word_t size, Reg reg);
extern void translate_syscall(Tracee *tracee);
extern int translate_syscall_enter(Tracee *tracee);
extern void translate_syscall_exit(Tracee *tracee);
#endif /* SYSCALL_H */
+150
View File
@@ -0,0 +1,150 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <assert.h>
#include "syscall/sysnum.h"
#include "tracee/tracee.h"
#include "tracee/abi.h"
#include "tracee/reg.h"
#include "arch.h"
#include "cli/note.h"
#include SYSNUMS_HEADER1
#ifdef SYSNUMS_HEADER2
#include SYSNUMS_HEADER2
#endif
typedef struct {
const Sysnum *table;
word_t offset;
word_t length;
} Sysnums;
/**
* Update @sysnums' fields with the sysnum table for the given @abi.
*/
static void get_sysnums(Abi abi, Sysnums *sysnums)
{
switch (abi) {
case ABI_DEFAULT:
sysnums->table = SYSNUMS_ABI1;
sysnums->length = sizeof(SYSNUMS_ABI1) / sizeof(Sysnum);
sysnums->offset = 0;
return;
#ifdef SYSNUMS_ABI2
case ABI_2:
sysnums->table = SYSNUMS_ABI2;
sysnums->length = sizeof(SYSNUMS_ABI2) / sizeof(Sysnum);
sysnums->offset = 0;
return;
#endif
default:
assert(0);
}
}
/**
* Return the neutral value of @sysnum from the given @abi.
*/
static Sysnum translate_sysnum(Abi abi, word_t sysnum)
{
Sysnums sysnums;
word_t index;
get_sysnums(abi, &sysnums);
/* Sanity checks. */
if (sysnum < sysnums.offset)
return PR_void;
index = sysnum - sysnums.offset;
/* Sanity checks. */
if (index > sysnums.length)
return PR_void;
return sysnums.table[index];
}
/**
* Return the architecture value of @sysnum for the given @abi.
*/
word_t detranslate_sysnum(Abi abi, Sysnum sysnum)
{
Sysnums sysnums;
size_t i;
/* Very special case. */
if (sysnum == PR_void)
return SYSCALL_AVOIDER;
get_sysnums(abi, &sysnums);
for (i = 0; i < sysnums.length; i++) {
if (sysnums.table[i] != sysnum)
continue;
return i + sysnums.offset;
}
return SYSCALL_AVOIDER;
}
/**
* Return the neutral value of the @tracee's current syscall number.
*/
Sysnum get_sysnum(const Tracee *tracee, RegVersion version)
{
return translate_sysnum(get_abi(tracee), peek_reg(tracee, version, SYSARG_NUM));
}
/**
* Overwrite the @tracee's current syscall number with @sysnum. Note:
* this neutral value is automatically converted into the architecture
* value.
*/
void set_sysnum(Tracee *tracee, Sysnum sysnum)
{
poke_reg(tracee, SYSARG_NUM, detranslate_sysnum(get_abi(tracee), sysnum));
}
/**
* Return the human readable name of @sysnum.
*/
const char *stringify_sysnum(Sysnum sysnum)
{
#define SYSNUM(item) [ PR_ ## item ] = #item,
static const char *names[] = {
#include "syscall/sysnums.list"
};
#undef SYSNUM
if (sysnum == 0)
return "void";
if (sysnum >= PR_NB_SYSNUM)
return "";
return names[sysnum];
}
@@ -0,0 +1,45 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SYSNUM_H
#define SYSNUM_H
#include <stdbool.h>
#include "tracee/tracee.h"
#include "tracee/abi.h"
#include "tracee/reg.h"
#define SYSNUM(item) PR_ ## item,
typedef enum {
PR_void = 0,
#include "syscall/sysnums.list"
PR_NB_SYSNUM
} Sysnum;
#undef SYSNUM
extern Sysnum get_sysnum(const Tracee *tracee, RegVersion version);
extern void set_sysnum(Tracee *tracee, Sysnum sysnum);
extern word_t detranslate_sysnum(Abi abi, Sysnum sysnum);
extern const char *stringify_sysnum(Sysnum sysnum);
#endif /* SYSNUM_H */
@@ -0,0 +1,360 @@
#include "syscall/sysnum.h"
static const Sysnum sysnums_arm[] = {
[ 0 ] = PR_restart_syscall,
[ 1 ] = PR_exit,
[ 2 ] = PR_fork,
[ 3 ] = PR_read,
[ 4 ] = PR_write,
[ 5 ] = PR_open,
[ 6 ] = PR_close,
[ 8 ] = PR_creat,
[ 9 ] = PR_link,
[ 10 ] = PR_unlink,
[ 11 ] = PR_execve,
[ 12 ] = PR_chdir,
[ 14 ] = PR_mknod,
[ 15 ] = PR_chmod,
[ 16 ] = PR_lchown,
[ 19 ] = PR_lseek,
[ 20 ] = PR_getpid,
[ 21 ] = PR_mount,
[ 23 ] = PR_setuid,
[ 24 ] = PR_getuid,
[ 26 ] = PR_ptrace,
[ 29 ] = PR_pause,
[ 30 ] = PR_utime,
[ 33 ] = PR_access,
[ 34 ] = PR_nice,
[ 36 ] = PR_sync,
[ 37 ] = PR_kill,
[ 38 ] = PR_rename,
[ 39 ] = PR_mkdir,
[ 40 ] = PR_rmdir,
[ 41 ] = PR_dup,
[ 42 ] = PR_pipe,
[ 43 ] = PR_times,
[ 45 ] = PR_brk,
[ 46 ] = PR_setgid,
[ 47 ] = PR_getgid,
[ 49 ] = PR_geteuid,
[ 50 ] = PR_getegid,
[ 51 ] = PR_acct,
[ 52 ] = PR_umount2,
[ 54 ] = PR_ioctl,
[ 55 ] = PR_fcntl,
[ 57 ] = PR_setpgid,
[ 60 ] = PR_umask,
[ 61 ] = PR_chroot,
[ 62 ] = PR_ustat,
[ 63 ] = PR_dup2,
[ 64 ] = PR_getppid,
[ 65 ] = PR_getpgrp,
[ 66 ] = PR_setsid,
[ 67 ] = PR_sigaction,
[ 70 ] = PR_setreuid,
[ 71 ] = PR_setregid,
[ 72 ] = PR_sigsuspend,
[ 73 ] = PR_sigpending,
[ 74 ] = PR_sethostname,
[ 75 ] = PR_setrlimit,
[ 77 ] = PR_getrusage,
[ 78 ] = PR_gettimeofday,
[ 79 ] = PR_settimeofday,
[ 80 ] = PR_getgroups,
[ 81 ] = PR_setgroups,
[ 83 ] = PR_symlink,
[ 85 ] = PR_readlink,
[ 86 ] = PR_uselib,
[ 87 ] = PR_swapon,
[ 88 ] = PR_reboot,
[ 91 ] = PR_munmap,
[ 92 ] = PR_truncate,
[ 93 ] = PR_ftruncate,
[ 94 ] = PR_fchmod,
[ 95 ] = PR_fchown,
[ 96 ] = PR_getpriority,
[ 97 ] = PR_setpriority,
[ 99 ] = PR_statfs,
[ 100 ] = PR_fstatfs,
[ 103 ] = PR_syslog,
[ 104 ] = PR_setitimer,
[ 105 ] = PR_getitimer,
[ 106 ] = PR_stat,
[ 107 ] = PR_lstat,
[ 108 ] = PR_fstat,
[ 111 ] = PR_vhangup,
[ 114 ] = PR_wait4,
[ 115 ] = PR_swapoff,
[ 116 ] = PR_sysinfo,
[ 118 ] = PR_fsync,
[ 119 ] = PR_sigreturn,
[ 120 ] = PR_clone,
[ 121 ] = PR_setdomainname,
[ 122 ] = PR_uname,
[ 124 ] = PR_adjtimex,
[ 125 ] = PR_mprotect,
[ 126 ] = PR_sigprocmask,
[ 128 ] = PR_init_module,
[ 129 ] = PR_delete_module,
[ 131 ] = PR_quotactl,
[ 132 ] = PR_getpgid,
[ 133 ] = PR_fchdir,
[ 134 ] = PR_bdflush,
[ 135 ] = PR_sysfs,
[ 136 ] = PR_personality,
[ 138 ] = PR_setfsuid,
[ 139 ] = PR_setfsgid,
[ 140 ] = PR__llseek,
[ 141 ] = PR_getdents,
[ 142 ] = PR__newselect,
[ 143 ] = PR_flock,
[ 144 ] = PR_msync,
[ 145 ] = PR_readv,
[ 146 ] = PR_writev,
[ 147 ] = PR_getsid,
[ 148 ] = PR_fdatasync,
[ 149 ] = PR__sysctl,
[ 150 ] = PR_mlock,
[ 151 ] = PR_munlock,
[ 152 ] = PR_mlockall,
[ 153 ] = PR_munlockall,
[ 154 ] = PR_sched_setparam,
[ 155 ] = PR_sched_getparam,
[ 156 ] = PR_sched_setscheduler,
[ 157 ] = PR_sched_getscheduler,
[ 158 ] = PR_sched_yield,
[ 159 ] = PR_sched_get_priority_max,
[ 160 ] = PR_sched_get_priority_min,
[ 161 ] = PR_sched_rr_get_interval,
[ 162 ] = PR_nanosleep,
[ 163 ] = PR_mremap,
[ 164 ] = PR_setresuid,
[ 165 ] = PR_getresuid,
[ 168 ] = PR_poll,
[ 169 ] = PR_nfsservctl,
[ 170 ] = PR_setresgid,
[ 171 ] = PR_getresgid,
[ 172 ] = PR_prctl,
[ 173 ] = PR_rt_sigreturn,
[ 174 ] = PR_rt_sigaction,
[ 175 ] = PR_rt_sigprocmask,
[ 176 ] = PR_rt_sigpending,
[ 177 ] = PR_rt_sigtimedwait,
[ 178 ] = PR_rt_sigqueueinfo,
[ 179 ] = PR_rt_sigsuspend,
[ 180 ] = PR_pread64,
[ 181 ] = PR_pwrite64,
[ 182 ] = PR_chown,
[ 183 ] = PR_getcwd,
[ 184 ] = PR_capget,
[ 185 ] = PR_capset,
[ 186 ] = PR_sigaltstack,
[ 187 ] = PR_sendfile,
[ 190 ] = PR_vfork,
[ 191 ] = PR_ugetrlimit,
[ 192 ] = PR_mmap2,
[ 193 ] = PR_truncate64,
[ 194 ] = PR_ftruncate64,
[ 195 ] = PR_stat64,
[ 196 ] = PR_lstat64,
[ 197 ] = PR_fstat64,
[ 198 ] = PR_lchown32,
[ 199 ] = PR_getuid32,
[ 200 ] = PR_getgid32,
[ 201 ] = PR_geteuid32,
[ 202 ] = PR_getegid32,
[ 203 ] = PR_setreuid32,
[ 204 ] = PR_setregid32,
[ 205 ] = PR_getgroups32,
[ 206 ] = PR_setgroups32,
[ 207 ] = PR_fchown32,
[ 208 ] = PR_setresuid32,
[ 209 ] = PR_getresuid32,
[ 210 ] = PR_setresgid32,
[ 211 ] = PR_getresgid32,
[ 212 ] = PR_chown32,
[ 213 ] = PR_setuid32,
[ 214 ] = PR_setgid32,
[ 215 ] = PR_setfsuid32,
[ 216 ] = PR_setfsgid32,
[ 217 ] = PR_getdents64,
[ 218 ] = PR_pivot_root,
[ 219 ] = PR_mincore,
[ 220 ] = PR_madvise,
[ 221 ] = PR_fcntl64,
[ 222 ] = PR_void,
[ 224 ] = PR_gettid,
[ 225 ] = PR_readahead,
[ 226 ] = PR_setxattr,
[ 227 ] = PR_lsetxattr,
[ 228 ] = PR_fsetxattr,
[ 229 ] = PR_getxattr,
[ 230 ] = PR_lgetxattr,
[ 231 ] = PR_fgetxattr,
[ 232 ] = PR_listxattr,
[ 233 ] = PR_llistxattr,
[ 234 ] = PR_flistxattr,
[ 235 ] = PR_removexattr,
[ 236 ] = PR_lremovexattr,
[ 237 ] = PR_fremovexattr,
[ 238 ] = PR_tkill,
[ 239 ] = PR_sendfile64,
[ 240 ] = PR_futex,
[ 241 ] = PR_sched_setaffinity,
[ 242 ] = PR_sched_getaffinity,
[ 243 ] = PR_io_setup,
[ 244 ] = PR_io_destroy,
[ 245 ] = PR_io_getevents,
[ 246 ] = PR_io_submit,
[ 247 ] = PR_io_cancel,
[ 248 ] = PR_exit_group,
[ 249 ] = PR_lookup_dcookie,
[ 250 ] = PR_epoll_create,
[ 251 ] = PR_epoll_ctl,
[ 252 ] = PR_epoll_wait,
[ 253 ] = PR_remap_file_pages,
[ 256 ] = PR_set_tid_address,
[ 257 ] = PR_timer_create,
[ 258 ] = PR_timer_settime,
[ 259 ] = PR_timer_gettime,
[ 260 ] = PR_timer_getoverrun,
[ 261 ] = PR_timer_delete,
[ 262 ] = PR_clock_settime,
[ 263 ] = PR_clock_gettime,
[ 264 ] = PR_clock_getres,
[ 265 ] = PR_clock_nanosleep,
[ 266 ] = PR_statfs64,
[ 267 ] = PR_fstatfs64,
[ 268 ] = PR_tgkill,
[ 269 ] = PR_utimes,
[ 270 ] = PR_arm_fadvise64_64,
[ 271 ] = PR_pciconfig_iobase,
[ 272 ] = PR_pciconfig_read,
[ 273 ] = PR_pciconfig_write,
[ 274 ] = PR_mq_open,
[ 275 ] = PR_mq_unlink,
[ 276 ] = PR_mq_timedsend,
[ 277 ] = PR_mq_timedreceive,
[ 278 ] = PR_mq_notify,
[ 279 ] = PR_mq_getsetattr,
[ 280 ] = PR_waitid,
[ 281 ] = PR_socket,
[ 282 ] = PR_bind,
[ 283 ] = PR_connect,
[ 284 ] = PR_listen,
[ 285 ] = PR_accept,
[ 286 ] = PR_getsockname,
[ 287 ] = PR_getpeername,
[ 288 ] = PR_socketpair,
[ 289 ] = PR_send,
[ 290 ] = PR_sendto,
[ 291 ] = PR_recv,
[ 292 ] = PR_recvfrom,
[ 293 ] = PR_shutdown,
[ 294 ] = PR_setsockopt,
[ 295 ] = PR_getsockopt,
[ 296 ] = PR_sendmsg,
[ 297 ] = PR_recvmsg,
[ 298 ] = PR_semop,
[ 299 ] = PR_semget,
[ 300 ] = PR_semctl,
[ 301 ] = PR_msgsnd,
[ 302 ] = PR_msgrcv,
[ 303 ] = PR_msgget,
[ 304 ] = PR_msgctl,
[ 305 ] = PR_shmat,
[ 306 ] = PR_shmdt,
[ 307 ] = PR_shmget,
[ 308 ] = PR_shmctl,
[ 309 ] = PR_add_key,
[ 310 ] = PR_request_key,
[ 311 ] = PR_keyctl,
[ 312 ] = PR_semtimedop,
[ 313 ] = PR_vserver,
[ 314 ] = PR_ioprio_set,
[ 315 ] = PR_ioprio_get,
[ 316 ] = PR_inotify_init,
[ 317 ] = PR_inotify_add_watch,
[ 318 ] = PR_inotify_rm_watch,
[ 319 ] = PR_mbind,
[ 320 ] = PR_get_mempolicy,
[ 321 ] = PR_set_mempolicy,
[ 322 ] = PR_openat,
[ 323 ] = PR_mkdirat,
[ 324 ] = PR_mknodat,
[ 325 ] = PR_fchownat,
[ 326 ] = PR_futimesat,
[ 327 ] = PR_fstatat64,
[ 328 ] = PR_unlinkat,
[ 329 ] = PR_renameat,
[ 330 ] = PR_linkat,
[ 331 ] = PR_symlinkat,
[ 332 ] = PR_readlinkat,
[ 333 ] = PR_fchmodat,
[ 334 ] = PR_faccessat,
[ 335 ] = PR_pselect6,
[ 336 ] = PR_ppoll,
[ 337 ] = PR_unshare,
[ 338 ] = PR_set_robust_list,
[ 339 ] = PR_get_robust_list,
[ 340 ] = PR_splice,
[ 341 ] = PR_arm_sync_file_range,
[ 342 ] = PR_tee,
[ 343 ] = PR_vmsplice,
[ 344 ] = PR_move_pages,
[ 345 ] = PR_getcpu,
[ 346 ] = PR_epoll_pwait,
[ 347 ] = PR_kexec_load,
[ 348 ] = PR_utimensat,
[ 349 ] = PR_signalfd,
[ 350 ] = PR_timerfd_create,
[ 351 ] = PR_eventfd,
[ 352 ] = PR_fallocate,
[ 353 ] = PR_timerfd_settime,
[ 354 ] = PR_timerfd_gettime,
[ 355 ] = PR_signalfd4,
[ 356 ] = PR_eventfd2,
[ 357 ] = PR_epoll_create1,
[ 358 ] = PR_dup3,
[ 359 ] = PR_pipe2,
[ 360 ] = PR_inotify_init1,
[ 361 ] = PR_preadv,
[ 362 ] = PR_pwritev,
[ 363 ] = PR_rt_tgsigqueueinfo,
[ 364 ] = PR_perf_event_open,
[ 365 ] = PR_recvmmsg,
[ 366 ] = PR_accept4,
[ 367 ] = PR_fanotify_init,
[ 368 ] = PR_fanotify_mark,
[ 369 ] = PR_prlimit64,
[ 370 ] = PR_name_to_handle_at,
[ 371 ] = PR_open_by_handle_at,
[ 372 ] = PR_clock_adjtime,
[ 373 ] = PR_syncfs,
[ 374 ] = PR_sendmmsg,
[ 375 ] = PR_setns,
[ 376 ] = PR_process_vm_readv,
[ 377 ] = PR_process_vm_writev,
[ 378 ] = PR_kcmp,
[ 379 ] = PR_finit_module,
[ 380 ] = PR_sched_setattr,
[ 381 ] = PR_sched_getattr,
[ 382 ] = PR_renameat2,
[ 383 ] = PR_seccomp,
[ 384 ] = PR_getrandom,
[ 385 ] = PR_memfd_create,
[ 386 ] = PR_bpf,
[ 387 ] = PR_execveat,
[ 388 ] = PR_userfaultfd,
[ 389 ] = PR_membarrier,
[ 390 ] = PR_mlock2,
[ 391 ] = PR_copy_file_range,
[ 392 ] = PR_preadv2,
[ 393 ] = PR_pwritev2,
[ 394 ] = PR_pkey_mprotect,
[ 395 ] = PR_pkey_alloc,
[ 396 ] = PR_pkey_free,
[ 397 ] = PR_statx,
[ 439 ] = PR_faccessat2,
};
@@ -0,0 +1,282 @@
#include "syscall/sysnum.h"
static const Sysnum sysnums_arm64[] = {
[ 0 ] = PR_io_setup,
[ 1 ] = PR_io_destroy,
[ 2 ] = PR_io_submit,
[ 3 ] = PR_io_cancel,
[ 4 ] = PR_io_getevents,
[ 5 ] = PR_setxattr,
[ 6 ] = PR_lsetxattr,
[ 7 ] = PR_fsetxattr,
[ 8 ] = PR_getxattr,
[ 9 ] = PR_lgetxattr,
[ 10 ] = PR_fgetxattr,
[ 11 ] = PR_listxattr,
[ 12 ] = PR_llistxattr,
[ 13 ] = PR_flistxattr,
[ 14 ] = PR_removexattr,
[ 15 ] = PR_lremovexattr,
[ 16 ] = PR_fremovexattr,
[ 17 ] = PR_getcwd,
[ 18 ] = PR_lookup_dcookie,
[ 19 ] = PR_eventfd2,
[ 20 ] = PR_epoll_create1,
[ 21 ] = PR_epoll_ctl,
[ 22 ] = PR_epoll_pwait,
[ 23 ] = PR_dup,
[ 24 ] = PR_dup3,
[ 25 ] = PR_fcntl,
[ 26 ] = PR_inotify_init1,
[ 27 ] = PR_inotify_add_watch,
[ 28 ] = PR_inotify_rm_watch,
[ 29 ] = PR_ioctl,
[ 30 ] = PR_ioprio_set,
[ 31 ] = PR_ioprio_get,
[ 32 ] = PR_flock,
[ 33 ] = PR_mknodat,
[ 34 ] = PR_mkdirat,
[ 35 ] = PR_unlinkat,
[ 36 ] = PR_symlinkat,
[ 37 ] = PR_linkat,
[ 38 ] = PR_renameat,
[ 39 ] = PR_umount2,
[ 40 ] = PR_mount,
[ 41 ] = PR_pivot_root,
[ 42 ] = PR_nfsservctl,
[ 43 ] = PR_statfs,
[ 44 ] = PR_fstatfs,
[ 45 ] = PR_truncate,
[ 46 ] = PR_ftruncate,
[ 47 ] = PR_fallocate,
[ 48 ] = PR_faccessat,
[ 49 ] = PR_chdir,
[ 50 ] = PR_fchdir,
[ 51 ] = PR_chroot,
[ 52 ] = PR_fchmod,
[ 53 ] = PR_fchmodat,
[ 54 ] = PR_fchownat,
[ 55 ] = PR_fchown,
[ 56 ] = PR_openat,
[ 57 ] = PR_close,
[ 58 ] = PR_vhangup,
[ 59 ] = PR_pipe2,
[ 60 ] = PR_quotactl,
[ 61 ] = PR_getdents64,
[ 62 ] = PR_lseek,
[ 63 ] = PR_read,
[ 64 ] = PR_write,
[ 65 ] = PR_readv,
[ 66 ] = PR_writev,
[ 67 ] = PR_pread64,
[ 68 ] = PR_pwrite64,
[ 69 ] = PR_preadv,
[ 70 ] = PR_pwritev,
[ 71 ] = PR_sendfile,
[ 72 ] = PR_pselect6,
[ 73 ] = PR_ppoll,
[ 74 ] = PR_signalfd4,
[ 75 ] = PR_vmsplice,
[ 76 ] = PR_splice,
[ 77 ] = PR_tee,
[ 78 ] = PR_readlinkat,
[ 79 ] = PR_fstatat64,
[ 80 ] = PR_fstat,
[ 81 ] = PR_sync,
[ 82 ] = PR_fsync,
[ 83 ] = PR_fdatasync,
[ 84 ] = PR_sync_file_range,
[ 85 ] = PR_timerfd_create,
[ 86 ] = PR_timerfd_settime,
[ 87 ] = PR_timerfd_gettime,
[ 88 ] = PR_utimensat,
[ 89 ] = PR_acct,
[ 90 ] = PR_capget,
[ 91 ] = PR_capset,
[ 92 ] = PR_personality,
[ 93 ] = PR_exit,
[ 94 ] = PR_exit_group,
[ 95 ] = PR_waitid,
[ 96 ] = PR_set_tid_address,
[ 97 ] = PR_unshare,
[ 98 ] = PR_futex,
[ 99 ] = PR_set_robust_list,
[ 100 ] = PR_get_robust_list,
[ 101 ] = PR_nanosleep,
[ 102 ] = PR_getitimer,
[ 103 ] = PR_setitimer,
[ 104 ] = PR_kexec_load,
[ 105 ] = PR_init_module,
[ 106 ] = PR_delete_module,
[ 107 ] = PR_timer_create,
[ 108 ] = PR_timer_gettime,
[ 109 ] = PR_timer_getoverrun,
[ 110 ] = PR_timer_settime,
[ 111 ] = PR_timer_delete,
[ 112 ] = PR_clock_settime,
[ 113 ] = PR_clock_gettime,
[ 114 ] = PR_clock_getres,
[ 115 ] = PR_clock_nanosleep,
[ 116 ] = PR_syslog,
[ 117 ] = PR_ptrace,
[ 118 ] = PR_sched_setparam,
[ 119 ] = PR_sched_setscheduler,
[ 120 ] = PR_sched_getscheduler,
[ 121 ] = PR_sched_getparam,
[ 122 ] = PR_sched_setaffinity,
[ 123 ] = PR_sched_getaffinity,
[ 124 ] = PR_sched_yield,
[ 125 ] = PR_sched_get_priority_max,
[ 126 ] = PR_sched_get_priority_min,
[ 127 ] = PR_sched_rr_get_interval,
[ 128 ] = PR_restart_syscall,
[ 129 ] = PR_kill,
[ 130 ] = PR_tkill,
[ 131 ] = PR_tgkill,
[ 132 ] = PR_sigaltstack,
[ 133 ] = PR_rt_sigsuspend,
[ 134 ] = PR_rt_sigaction,
[ 135 ] = PR_rt_sigprocmask,
[ 136 ] = PR_rt_sigpending,
[ 137 ] = PR_rt_sigtimedwait,
[ 138 ] = PR_rt_sigqueueinfo,
[ 139 ] = PR_rt_sigreturn,
[ 140 ] = PR_setpriority,
[ 141 ] = PR_getpriority,
[ 142 ] = PR_reboot,
[ 143 ] = PR_setregid,
[ 144 ] = PR_setgid,
[ 145 ] = PR_setreuid,
[ 146 ] = PR_setuid,
[ 147 ] = PR_setresuid,
[ 148 ] = PR_getresuid,
[ 149 ] = PR_setresgid,
[ 150 ] = PR_getresgid,
[ 151 ] = PR_setfsuid,
[ 152 ] = PR_setfsgid,
[ 153 ] = PR_times,
[ 154 ] = PR_setpgid,
[ 155 ] = PR_getpgid,
[ 156 ] = PR_getsid,
[ 157 ] = PR_setsid,
[ 158 ] = PR_getgroups,
[ 159 ] = PR_setgroups,
[ 160 ] = PR_uname,
[ 161 ] = PR_sethostname,
[ 162 ] = PR_setdomainname,
[ 163 ] = PR_getrlimit,
[ 164 ] = PR_setrlimit,
[ 165 ] = PR_getrusage,
[ 166 ] = PR_umask,
[ 167 ] = PR_prctl,
[ 168 ] = PR_getcpu,
[ 169 ] = PR_gettimeofday,
[ 170 ] = PR_settimeofday,
[ 171 ] = PR_adjtimex,
[ 172 ] = PR_getpid,
[ 173 ] = PR_getppid,
[ 174 ] = PR_getuid,
[ 175 ] = PR_geteuid,
[ 176 ] = PR_getgid,
[ 177 ] = PR_getegid,
[ 178 ] = PR_gettid,
[ 179 ] = PR_sysinfo,
[ 180 ] = PR_mq_open,
[ 181 ] = PR_mq_unlink,
[ 182 ] = PR_mq_timedsend,
[ 183 ] = PR_mq_timedreceive,
[ 184 ] = PR_mq_notify,
[ 185 ] = PR_mq_getsetattr,
[ 186 ] = PR_msgget,
[ 187 ] = PR_msgctl,
[ 188 ] = PR_msgrcv,
[ 189 ] = PR_msgsnd,
[ 190 ] = PR_semget,
[ 191 ] = PR_semctl,
[ 192 ] = PR_semtimedop,
[ 193 ] = PR_semop,
[ 194 ] = PR_shmget,
[ 195 ] = PR_shmctl,
[ 196 ] = PR_shmat,
[ 197 ] = PR_shmdt,
[ 198 ] = PR_socket,
[ 199 ] = PR_socketpair,
[ 200 ] = PR_bind,
[ 201 ] = PR_listen,
[ 202 ] = PR_accept,
[ 203 ] = PR_connect,
[ 204 ] = PR_getsockname,
[ 205 ] = PR_getpeername,
[ 206 ] = PR_sendto,
[ 207 ] = PR_recvfrom,
[ 208 ] = PR_setsockopt,
[ 209 ] = PR_getsockopt,
[ 210 ] = PR_shutdown,
[ 211 ] = PR_sendmsg,
[ 212 ] = PR_recvmsg,
[ 213 ] = PR_readahead,
[ 214 ] = PR_brk,
[ 215 ] = PR_munmap,
[ 216 ] = PR_mremap,
[ 217 ] = PR_add_key,
[ 218 ] = PR_request_key,
[ 219 ] = PR_keyctl,
[ 220 ] = PR_clone,
[ 221 ] = PR_execve,
[ 222 ] = PR_mmap,
[ 223 ] = PR_fadvise64,
[ 224 ] = PR_swapon,
[ 225 ] = PR_swapoff,
[ 226 ] = PR_mprotect,
[ 227 ] = PR_msync,
[ 228 ] = PR_mlock,
[ 229 ] = PR_munlock,
[ 230 ] = PR_mlockall,
[ 231 ] = PR_munlockall,
[ 232 ] = PR_mincore,
[ 233 ] = PR_madvise,
[ 234 ] = PR_remap_file_pages,
[ 235 ] = PR_mbind,
[ 236 ] = PR_get_mempolicy,
[ 237 ] = PR_set_mempolicy,
[ 238 ] = PR_migrate_pages,
[ 239 ] = PR_move_pages,
[ 240 ] = PR_rt_tgsigqueueinfo,
[ 241 ] = PR_perf_event_open,
[ 242 ] = PR_accept4,
[ 243 ] = PR_recvmmsg,
[ 244 ] = PR_arch_specific_syscall,
[ 260 ] = PR_wait4,
[ 261 ] = PR_prlimit64,
[ 262 ] = PR_fanotify_init,
[ 263 ] = PR_fanotify_mark,
[ 264 ] = PR_name_to_handle_at,
[ 265 ] = PR_open_by_handle_at,
[ 266 ] = PR_clock_adjtime,
[ 267 ] = PR_syncfs,
[ 268 ] = PR_setns,
[ 269 ] = PR_sendmmsg,
[ 270 ] = PR_process_vm_readv,
[ 271 ] = PR_process_vm_writev,
[ 272 ] = PR_kcmp,
[ 273 ] = PR_finit_module,
[ 274 ] = PR_sched_setattr,
[ 275 ] = PR_sched_getattr,
[ 276 ] = PR_renameat2,
[ 277 ] = PR_seccomp,
[ 278 ] = PR_getrandom,
[ 279 ] = PR_memfd_create,
[ 280 ] = PR_bpf,
[ 281 ] = PR_execveat,
[ 282 ] = PR_userfaultfd,
[ 283 ] = PR_membarrier,
[ 284 ] = PR_mlock2,
[ 285 ] = PR_copy_file_range,
[ 286 ] = PR_preadv2,
[ 287 ] = PR_pwritev2,
[ 288 ] = PR_pkey_mprotect,
[ 289 ] = PR_pkey_alloc,
[ 290 ] = PR_pkey_free,
[ 291 ] = PR_statx,
[ 439 ] = PR_faccessat2,
};
@@ -0,0 +1,447 @@
SYSNUM(ARM_BASE)
SYSNUM(ARM_breakpoint)
SYSNUM(ARM_cacheflush)
SYSNUM(ARM_set_tls)
SYSNUM(ARM_usr26)
SYSNUM(ARM_usr32)
SYSNUM(X32_SYSCALL_BIT)
SYSNUM(_llseek)
SYSNUM(_newselect)
SYSNUM(_sysctl)
SYSNUM(accept)
SYSNUM(accept4)
SYSNUM(access)
SYSNUM(acct)
SYSNUM(add_key)
SYSNUM(adjtimex)
SYSNUM(afs_syscall)
SYSNUM(alarm)
SYSNUM(arch_prctl)
SYSNUM(arch_specific_syscall)
SYSNUM(arm_fadvise64_64)
SYSNUM(arm_sync_file_range)
SYSNUM(bdflush)
SYSNUM(bind)
SYSNUM(bpf)
SYSNUM(break)
SYSNUM(brk)
SYSNUM(cacheflush)
SYSNUM(capget)
SYSNUM(capset)
SYSNUM(chdir)
SYSNUM(chmod)
SYSNUM(chown)
SYSNUM(chown32)
SYSNUM(chroot)
SYSNUM(clock_adjtime)
SYSNUM(clock_getres)
SYSNUM(clock_gettime)
SYSNUM(clock_nanosleep)
SYSNUM(clock_settime)
SYSNUM(clone)
SYSNUM(close)
SYSNUM(connect)
SYSNUM(copy_file_range)
SYSNUM(creat)
SYSNUM(create_module)
SYSNUM(delete_module)
SYSNUM(dup)
SYSNUM(dup2)
SYSNUM(dup3)
SYSNUM(epoll_create)
SYSNUM(epoll_create1)
SYSNUM(epoll_ctl)
SYSNUM(epoll_ctl_old)
SYSNUM(epoll_pwait)
SYSNUM(epoll_wait)
SYSNUM(epoll_wait_old)
SYSNUM(eventfd)
SYSNUM(eventfd2)
SYSNUM(execve)
SYSNUM(execveat)
SYSNUM(exit)
SYSNUM(exit_group)
SYSNUM(faccessat)
SYSNUM(faccessat2)
SYSNUM(fadvise64)
SYSNUM(fadvise64_64)
SYSNUM(fallocate)
SYSNUM(fanotify_init)
SYSNUM(fanotify_mark)
SYSNUM(fchdir)
SYSNUM(fchmod)
SYSNUM(fchmodat)
SYSNUM(fchown)
SYSNUM(fchown32)
SYSNUM(fchownat)
SYSNUM(fcntl)
SYSNUM(fcntl64)
SYSNUM(fdatasync)
SYSNUM(fgetxattr)
SYSNUM(finit_module)
SYSNUM(flistxattr)
SYSNUM(flock)
SYSNUM(fork)
SYSNUM(fremovexattr)
SYSNUM(fsetxattr)
SYSNUM(fstat)
SYSNUM(fstat64)
SYSNUM(fstatat64)
SYSNUM(fstatfs)
SYSNUM(fstatfs64)
SYSNUM(fsync)
SYSNUM(ftime)
SYSNUM(ftruncate)
SYSNUM(ftruncate64)
SYSNUM(futex)
SYSNUM(futimesat)
SYSNUM(get_kernel_syms)
SYSNUM(get_mempolicy)
SYSNUM(get_robust_list)
SYSNUM(get_thread_area)
SYSNUM(getcpu)
SYSNUM(getcwd)
SYSNUM(getdents)
SYSNUM(getdents64)
SYSNUM(getegid)
SYSNUM(getegid32)
SYSNUM(geteuid)
SYSNUM(geteuid32)
SYSNUM(getgid)
SYSNUM(getgid32)
SYSNUM(getgroups)
SYSNUM(getgroups32)
SYSNUM(getitimer)
SYSNUM(getpeername)
SYSNUM(getpgid)
SYSNUM(getpgrp)
SYSNUM(getpid)
SYSNUM(getpmsg)
SYSNUM(getppid)
SYSNUM(getpriority)
SYSNUM(getrandom)
SYSNUM(getresgid)
SYSNUM(getresgid32)
SYSNUM(getresuid)
SYSNUM(getresuid32)
SYSNUM(getrlimit)
SYSNUM(getrusage)
SYSNUM(getsid)
SYSNUM(getsockname)
SYSNUM(getsockopt)
SYSNUM(gettid)
SYSNUM(gettimeofday)
SYSNUM(getuid)
SYSNUM(getuid32)
SYSNUM(getxattr)
SYSNUM(gtty)
SYSNUM(idle)
SYSNUM(init_module)
SYSNUM(inotify_add_watch)
SYSNUM(inotify_init)
SYSNUM(inotify_init1)
SYSNUM(inotify_rm_watch)
SYSNUM(io_cancel)
SYSNUM(io_destroy)
SYSNUM(io_getevents)
SYSNUM(io_setup)
SYSNUM(io_submit)
SYSNUM(ioctl)
SYSNUM(ioperm)
SYSNUM(iopl)
SYSNUM(ioprio_get)
SYSNUM(ioprio_set)
SYSNUM(ipc)
SYSNUM(kcmp)
SYSNUM(kexec_file_load)
SYSNUM(kexec_load)
SYSNUM(keyctl)
SYSNUM(kill)
SYSNUM(lchown)
SYSNUM(lchown32)
SYSNUM(lgetxattr)
SYSNUM(link)
SYSNUM(linkat)
SYSNUM(listen)
SYSNUM(listxattr)
SYSNUM(llistxattr)
SYSNUM(lock)
SYSNUM(lookup_dcookie)
SYSNUM(lremovexattr)
SYSNUM(lseek)
SYSNUM(lsetxattr)
SYSNUM(lstat)
SYSNUM(lstat64)
SYSNUM(madvise)
SYSNUM(mbind)
SYSNUM(membarrier)
SYSNUM(memfd_create)
SYSNUM(migrate_pages)
SYSNUM(mincore)
SYSNUM(mkdir)
SYSNUM(mkdirat)
SYSNUM(mknod)
SYSNUM(mknodat)
SYSNUM(mlock)
SYSNUM(mlock2)
SYSNUM(mlockall)
SYSNUM(mmap)
SYSNUM(mmap2)
SYSNUM(modify_ldt)
SYSNUM(mount)
SYSNUM(move_pages)
SYSNUM(mprotect)
SYSNUM(mpx)
SYSNUM(mq_getsetattr)
SYSNUM(mq_notify)
SYSNUM(mq_open)
SYSNUM(mq_timedreceive)
SYSNUM(mq_timedsend)
SYSNUM(mq_unlink)
SYSNUM(mremap)
SYSNUM(msgctl)
SYSNUM(msgget)
SYSNUM(msgrcv)
SYSNUM(msgsnd)
SYSNUM(msync)
SYSNUM(munlock)
SYSNUM(munlockall)
SYSNUM(munmap)
SYSNUM(name_to_handle_at)
SYSNUM(nanosleep)
SYSNUM(newfstatat)
SYSNUM(nfsservctl)
SYSNUM(nice)
SYSNUM(oldfstat)
SYSNUM(oldlstat)
SYSNUM(oldolduname)
SYSNUM(oldstat)
SYSNUM(olduname)
SYSNUM(open)
SYSNUM(open_by_handle_at)
SYSNUM(openat)
SYSNUM(pause)
SYSNUM(pciconfig_iobase)
SYSNUM(pciconfig_read)
SYSNUM(pciconfig_write)
SYSNUM(perf_event_open)
SYSNUM(personality)
SYSNUM(pipe)
SYSNUM(pipe2)
SYSNUM(pivot_root)
SYSNUM(pkey_alloc)
SYSNUM(pkey_free)
SYSNUM(pkey_mprotect)
SYSNUM(poll)
SYSNUM(ppoll)
SYSNUM(prctl)
SYSNUM(pread64)
SYSNUM(preadv)
SYSNUM(preadv2)
SYSNUM(prlimit64)
SYSNUM(process_vm_readv)
SYSNUM(process_vm_writev)
SYSNUM(prof)
SYSNUM(profil)
SYSNUM(pselect6)
SYSNUM(ptrace)
SYSNUM(putpmsg)
SYSNUM(pwrite64)
SYSNUM(pwritev)
SYSNUM(pwritev2)
SYSNUM(query_module)
SYSNUM(quotactl)
SYSNUM(read)
SYSNUM(readahead)
SYSNUM(readdir)
SYSNUM(readlink)
SYSNUM(readlinkat)
SYSNUM(readv)
SYSNUM(reboot)
SYSNUM(recv)
SYSNUM(recvfrom)
SYSNUM(recvmmsg)
SYSNUM(recvmsg)
SYSNUM(remap_file_pages)
SYSNUM(removexattr)
SYSNUM(rename)
SYSNUM(renameat)
SYSNUM(renameat2)
SYSNUM(request_key)
SYSNUM(restart_syscall)
SYSNUM(rmdir)
SYSNUM(rt_sigaction)
SYSNUM(rt_sigpending)
SYSNUM(rt_sigprocmask)
SYSNUM(rt_sigqueueinfo)
SYSNUM(rt_sigreturn)
SYSNUM(rt_sigsuspend)
SYSNUM(rt_sigtimedwait)
SYSNUM(rt_tgsigqueueinfo)
SYSNUM(sched_get_priority_max)
SYSNUM(sched_get_priority_min)
SYSNUM(sched_getaffinity)
SYSNUM(sched_getattr)
SYSNUM(sched_getparam)
SYSNUM(sched_getscheduler)
SYSNUM(sched_rr_get_interval)
SYSNUM(sched_setaffinity)
SYSNUM(sched_setattr)
SYSNUM(sched_setparam)
SYSNUM(sched_setscheduler)
SYSNUM(sched_yield)
SYSNUM(seccomp)
SYSNUM(security)
SYSNUM(select)
SYSNUM(semctl)
SYSNUM(semget)
SYSNUM(semop)
SYSNUM(semtimedop)
SYSNUM(send)
SYSNUM(sendfile)
SYSNUM(sendfile64)
SYSNUM(sendmmsg)
SYSNUM(sendmsg)
SYSNUM(sendto)
SYSNUM(set_mempolicy)
SYSNUM(set_robust_list)
SYSNUM(set_thread_area)
SYSNUM(set_tid_address)
SYSNUM(setdomainname)
SYSNUM(setfsgid)
SYSNUM(setfsgid32)
SYSNUM(setfsuid)
SYSNUM(setfsuid32)
SYSNUM(setgid)
SYSNUM(setgid32)
SYSNUM(setgroups)
SYSNUM(setgroups32)
SYSNUM(sethostname)
SYSNUM(setitimer)
SYSNUM(setns)
SYSNUM(setpgid)
SYSNUM(setpriority)
SYSNUM(setregid)
SYSNUM(setregid32)
SYSNUM(setresgid)
SYSNUM(setresgid32)
SYSNUM(setresuid)
SYSNUM(setresuid32)
SYSNUM(setreuid)
SYSNUM(setreuid32)
SYSNUM(setrlimit)
SYSNUM(setsid)
SYSNUM(setsockopt)
SYSNUM(settimeofday)
SYSNUM(setuid)
SYSNUM(setuid32)
SYSNUM(setxattr)
SYSNUM(sgetmask)
SYSNUM(shmat)
SYSNUM(shmctl)
SYSNUM(shmdt)
SYSNUM(shmget)
SYSNUM(shutdown)
SYSNUM(sigaction)
SYSNUM(sigaltstack)
SYSNUM(signal)
SYSNUM(signalfd)
SYSNUM(signalfd4)
SYSNUM(sigpending)
SYSNUM(sigprocmask)
SYSNUM(sigreturn)
SYSNUM(sigsuspend)
SYSNUM(socket)
SYSNUM(socketcall)
SYSNUM(socketpair)
SYSNUM(splice)
SYSNUM(ssetmask)
SYSNUM(stat)
SYSNUM(stat64)
SYSNUM(statfs)
SYSNUM(statfs64)
SYSNUM(statx)
SYSNUM(stime)
SYSNUM(stty)
SYSNUM(swapoff)
SYSNUM(swapon)
SYSNUM(symlink)
SYSNUM(symlinkat)
SYSNUM(sync)
SYSNUM(sync_file_range)
SYSNUM(sync_file_range2)
SYSNUM(syncfs)
SYSNUM(sysfs)
SYSNUM(sysinfo)
SYSNUM(syslog)
SYSNUM(tee)
SYSNUM(tgkill)
SYSNUM(time)
SYSNUM(timer_create)
SYSNUM(timer_delete)
SYSNUM(timer_getoverrun)
SYSNUM(timer_gettime)
SYSNUM(timer_settime)
SYSNUM(timerfd_create)
SYSNUM(timerfd_gettime)
SYSNUM(timerfd_settime)
SYSNUM(times)
SYSNUM(tkill)
SYSNUM(truncate)
SYSNUM(truncate64)
SYSNUM(tuxcall)
SYSNUM(ugetrlimit)
SYSNUM(ulimit)
SYSNUM(umask)
SYSNUM(umount)
SYSNUM(umount2)
SYSNUM(uname)
SYSNUM(unlink)
SYSNUM(unlinkat)
SYSNUM(unshare)
SYSNUM(uselib)
SYSNUM(userfaultfd)
SYSNUM(ustat)
SYSNUM(utime)
SYSNUM(utimensat)
SYSNUM(utimes)
SYSNUM(vfork)
SYSNUM(vhangup)
SYSNUM(vm86)
SYSNUM(vm86old)
SYSNUM(vmsplice)
SYSNUM(vserver)
SYSNUM(wait4)
SYSNUM(waitid)
SYSNUM(waitpid)
SYSNUM(write)
SYSNUM(writev)
SYSNUM(x32_execve)
SYSNUM(x32_get_robust_list)
SYSNUM(x32_ioctl)
SYSNUM(x32_kexec_load)
SYSNUM(x32_move_pages)
SYSNUM(x32_mq_notify)
SYSNUM(x32_preadv)
SYSNUM(x32_process_vm_readv)
SYSNUM(x32_process_vm_writev)
SYSNUM(x32_ptrace)
SYSNUM(x32_pwritev)
SYSNUM(x32_readv)
SYSNUM(x32_recvfrom)
SYSNUM(x32_recvmmsg)
SYSNUM(x32_recvmsg)
SYSNUM(x32_rt_sigaction)
SYSNUM(x32_rt_sigpending)
SYSNUM(x32_rt_sigqueueinfo)
SYSNUM(x32_rt_sigreturn)
SYSNUM(x32_rt_sigtimedwait)
SYSNUM(x32_rt_tgsigqueueinfo)
SYSNUM(x32_sendmmsg)
SYSNUM(x32_sendmsg)
SYSNUM(x32_set_robust_list)
SYSNUM(x32_sigaltstack)
SYSNUM(x32_timer_create)
SYSNUM(x32_vmsplice)
SYSNUM(x32_waitid)
SYSNUM(x32_writev)
+82
View File
@@ -0,0 +1,82 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef TRACEE_ABI_H
#define TRACEE_ABI_H
#include <stdbool.h>
#include <stddef.h> /* offsetof(), */
#include <sys/stat.h>
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "arch.h"
#include "attribute.h"
typedef enum {
ABI_DEFAULT = 0,
ABI_2, /* ARM EABI on AArch64. */
NB_MAX_ABIS,
} Abi;
/**
* Return the ABI currently used by the given @tracee.
*/
#if defined(ARCH_ARM64)
static inline Abi get_abi(const Tracee *tracee)
{
return tracee->is_aarch32 ? ABI_2 : ABI_DEFAULT;
}
/**
* Return true if @tracee is a 32-bit process running on a 64-bit
* kernel.
*/
static inline bool is_32on64_mode(const Tracee *tracee)
{
return tracee->is_aarch32;
}
#else
static inline Abi get_abi(const Tracee *tracee UNUSED)
{
return ABI_DEFAULT;
}
static inline bool is_32on64_mode(const Tracee *tracee UNUSED)
{
return false;
}
#endif
/**
* Return the size of a word according to the ABI currently used by
* the given @tracee.
*/
static inline size_t sizeof_word(const Tracee *tracee)
{
return (is_32on64_mode(tracee)
? sizeof(word_t) / 2
: sizeof(word_t));
}
#endif /* TRACEE_ABI_H */
+671
View File
@@ -0,0 +1,671 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sched.h> /* CLONE_*, */
#include <sys/types.h> /* pid_t, */
#include <sys/ptrace.h> /* ptrace(1), PTRACE_*, */
#include <sys/types.h> /* waitpid(2), */
#include <sys/wait.h> /* waitpid(2), */
#include <sys/utsname.h> /* uname(2), */
#include <unistd.h> /* fork(2), chdir(2), getpid(2), */
#include <string.h> /* strcmp(3), */
#include <errno.h> /* errno(3), */
#include <stdbool.h> /* bool, true, false, */
#include <assert.h> /* assert(3), */
#include <stdlib.h> /* atexit(3), getenv(3), */
#include <talloc.h> /* talloc_*, */
#include <inttypes.h> /* PRI*, */
#include <strings.h> /* bzero(3), */
#include "tracee/event.h"
#include "tracee/seccomp.h"
#include "tracee/mem.h"
#include "cli/note.h"
#include "path/path.h"
#include "path/binding.h"
#include "syscall/syscall.h"
#include "syscall/seccomp.h"
#include "ptrace/wait.h"
#include "execve/elf.h"
#include "attribute.h"
#include "compat.h"
static bool seccomp_after_ptrace_enter = false;
/**
* Start @tracee->exe with the given @argv[]. This function
* returns -errno if an error occurred, otherwise 0.
*/
int launch_process(Tracee *tracee, char *const argv[])
{
char *const default_argv[] = { "-sh", NULL };
long status;
pid_t pid;
/* Warn about open file descriptors. They won't be
* translated until they are closed. */
if (tracee->verbose > 0)
list_open_fd(tracee);
pid = fork();
switch(pid) {
case -1:
note(tracee, ERROR, SYSTEM, "fork()");
return -errno;
case 0: /* child */
/* Declare myself as ptraceable before executing the
* requested program. */
status = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if (status < 0) {
note(tracee, ERROR, SYSTEM, "ptrace(TRACEME)");
return -errno;
}
/* Synchronize with the tracer's event loop. Without
* this trick the tracer only sees the "return" from
* the next execve(2) so PRoot wouldn't handle the
* interpreter/runner. I also verified that strace
* does the same thing. */
kill(getpid(), SIGSTOP);
/* Improve performance by using seccomp mode 2 */
enable_syscall_filtering(tracee);
/* Now process is ptraced, so the current rootfs is already the
* guest rootfs. Note: Valgrind can't handle execve(2) on
* "foreign" binaries (ENOEXEC) but can handle execvp(3) on such
* binaries. */
execvp(tracee->exe, argv[0] != NULL ? argv : default_argv);
return -errno;
default: /* parent */
/* We know the pid of the first tracee now. */
tracee->pid = pid;
return 0;
}
/* Never reached. */
return -ENOSYS;
}
/* Send the KILL signal to all tracees when PRoot has received a fatal
* signal. */
static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED)
{
note(NULL, WARNING, INTERNAL, "signal %d received from process %d",
signum, siginfo->si_pid);
kill_all_tracees();
/* Exit immediately for system signals (segmentation fault,
* illegal instruction, ...), otherwise exit cleanly through
* the event loop. */
if (signum != SIGQUIT)
_exit(EXIT_FAILURE);
}
/**
* Helper for print_talloc_hierarchy().
*/
static void print_talloc_chunk(const void *ptr, int depth, int max_depth UNUSED,
int is_ref, void *data UNUSED)
{
const char *name;
size_t count;
size_t size;
name = talloc_get_name(ptr);
size = talloc_get_size(ptr);
count = talloc_reference_count(ptr);
if (depth == 0)
return;
while (depth-- > 1)
fprintf(stderr, "\t");
fprintf(stderr, "%-16s ", name);
if (is_ref)
fprintf(stderr, "-> %-8p", ptr);
else {
fprintf(stderr, "%-8p %zd bytes %zd ref'", ptr, size, count);
if (name[0] == '$') {
fprintf(stderr, "\t(\"%s\")", (char *)ptr);
}
if (name[0] == '@') {
char **argv;
int i;
fprintf(stderr, "\t(");
for (i = 0, argv = (char **)ptr; argv[i] != NULL; i++)
fprintf(stderr, "\"%s\", ", argv[i]);
fprintf(stderr, ")");
}
else if (strcmp(name, "Tracee") == 0) {
fprintf(stderr, "\t(pid = %d, parent = %p)",
((Tracee *)ptr)->pid, ((Tracee *)ptr)->parent);
}
else if (strcmp(name, "Bindings") == 0) {
Tracee *tracee;
tracee = TRACEE(ptr);
if (ptr == tracee->fs->bindings.pending)
fprintf(stderr, "\t(pending)");
else if (ptr == tracee->fs->bindings.guest)
fprintf(stderr, "\t(guest)");
else if (ptr == tracee->fs->bindings.host)
fprintf(stderr, "\t(host)");
}
else if (strcmp(name, "Binding") == 0) {
Binding *binding = (Binding *)ptr;
fprintf(stderr, "\t(%s:%s)", binding->host.path, binding->guest.path);
}
}
fprintf(stderr, "\n");
}
/* Print on stderr the complete talloc hierarchy. */
static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED)
{
switch (signum) {
case SIGUSR1:
talloc_report_depth_cb(NULL, 0, 100, print_talloc_chunk, NULL);
break;
case SIGUSR2:
talloc_report_depth_file(NULL, 0, 100, stderr);
break;
default:
break;
}
}
static int last_exit_status = -1;
/**
* Wait then handle any event from any tracee. This function returns
* the exit status of the last terminated program.
*/
int event_loop()
{
struct sigaction signal_action;
long status;
int signum;
/* Kill all tracees when exiting. */
status = atexit(kill_all_tracees);
if (status != 0)
note(NULL, WARNING, INTERNAL, "atexit() failed");
/* All signals are blocked when the signal handler is called.
* SIGINFO is used to know which process has signaled us and
* RESTART is used to restart waitpid(2) seamlessly. */
bzero(&signal_action, sizeof(signal_action));
signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
status = sigfillset(&signal_action.sa_mask);
if (status < 0)
note(NULL, WARNING, SYSTEM, "sigfillset()");
/* Handle all signals. */
for (signum = 0; signum < SIGRTMAX; signum++) {
switch (signum) {
case SIGQUIT:
case SIGILL:
case SIGABRT:
case SIGFPE:
case SIGSEGV:
/* Kill all tracees on abnormal termination
* signals. This ensures no process is left
* untraced. */
signal_action.sa_sigaction = kill_all_tracees2;
break;
case SIGUSR1:
case SIGUSR2:
/* Print on stderr the complete talloc
* hierarchy, useful for debug purpose. */
signal_action.sa_sigaction = print_talloc_hierarchy;
break;
case SIGCHLD:
case SIGCONT:
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
/* The default action is OK for these signals,
* they are related to tty and job control. */
continue;
default:
/* Ignore all other signals, including
* terminating ones (^C for instance). */
signal_action.sa_sigaction = (void *)SIG_IGN;
break;
}
status = sigaction(signum, &signal_action, NULL);
if (status < 0 && errno != EINVAL)
note(NULL, WARNING, SYSTEM, "sigaction(%d)", signum);
}
while (1) {
int tracee_status;
Tracee *tracee;
int signal;
pid_t pid;
/* This is the only safe place to free tracees. */
free_terminated_tracees();
/* Wait for the next tracee's stop. */
pid = waitpid(-1, &tracee_status, __WALL);
if (pid < 0) {
if (errno != ECHILD) {
note(NULL, ERROR, SYSTEM, "waitpid()");
return EXIT_FAILURE;
}
break;
}
/* Get information about this tracee. */
tracee = get_tracee(NULL, pid, true);
assert(tracee != NULL);
tracee->running = false;
if (tracee->as_ptracee.ptracer != NULL) {
bool keep_stopped = handle_ptracee_event(tracee, tracee_status);
if (keep_stopped)
continue;
}
signal = handle_tracee_event(tracee, tracee_status);
(void) restart_tracee(tracee, signal);
}
return last_exit_status;
}
/**
* Handle the current event (@tracee_status) of the given @tracee.
* This function returns the "computed" signal that should be used to
* restart the given @tracee.
*/
int handle_tracee_event(Tracee *tracee, int tracee_status)
{
static bool seccomp_detected = false;
static bool seccomp_after_ptrace_enter_checked = false;
long status;
int signal;
bool sysexit_necessary;
if (!seccomp_after_ptrace_enter_checked) {
seccomp_after_ptrace_enter = getenv("PROOT_ASSUME_NEW_SECCOMP") != NULL;
seccomp_after_ptrace_enter_checked = true;
}
/* When seccomp is enabled, all events are restarted in
* non-stop mode, but this default choice could be overwritten
* later if necessary. The check against "sysexit_pending"
* ensures PTRACE_SYSCALL (used to hit the exit stage under
* seccomp) is not cleared due to an event that would happen
* before the exit stage, eg. PTRACE_EVENT_EXEC for the exit
* stage of execve(2). */
sysexit_necessary = tracee->sysexit_pending
|| tracee->chain.syscalls != NULL
|| tracee->restore_original_regs_after_seccomp_event;
/* Don't overwrite restart_how if it is explicitly set
* elsewhere, i.e in the ptrace emulation when single
* stepping. */
if (tracee->restart_how == 0) {
if (tracee->seccomp == ENABLED && !sysexit_necessary)
tracee->restart_how = PTRACE_CONT;
else
tracee->restart_how = PTRACE_SYSCALL;
}
/* Not a signal-stop by default. */
signal = 0;
if (WIFEXITED(tracee_status)) {
last_exit_status = WEXITSTATUS(tracee_status);
VERBOSE(tracee, 1, "pid %d: exited with status %d", tracee->pid, last_exit_status);
terminate_tracee(tracee);
}
else if (WIFSIGNALED(tracee_status)) {
VERBOSE(tracee, (int) (tracee->pid != 1), "pid %d: terminated with signal %d", tracee->pid, WTERMSIG(tracee_status));
terminate_tracee(tracee);
}
else if (WIFSTOPPED(tracee_status)) {
/* Don't use WSTOPSIG() to extract the signal
* since it clears the PTRACE_EVENT_* bits. */
signal = (tracee_status & 0xfff00) >> 8;
switch (signal) {
static bool deliver_sigtrap = false;
case SIGTRAP: {
const unsigned long default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK |
PTRACE_O_TRACEVFORKDONE |
PTRACE_O_TRACEEXEC |
PTRACE_O_TRACECLONE |
PTRACE_O_TRACEEXIT);
/* Distinguish some events from others and
* automatically trace each new process with
* the same options.
*
* Note that only the first bare SIGTRAP is
* related to the tracing loop, others SIGTRAP
* carry tracing information because of
* TRACE*FORK/CLONE/EXEC. */
if (deliver_sigtrap)
break; /* Deliver this signal as-is. */
deliver_sigtrap = true;
/* Try to enable seccomp mode 2... */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options | PTRACE_O_TRACESECCOMP);
if (status < 0) {
/* ... otherwise use default options only. */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options);
if (status < 0) {
note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
exit(EXIT_FAILURE);
}
}
}
/* Fall through. */
case SIGTRAP | 0x80:
signal = 0;
/* This tracee got signaled then freed during the
sysenter stage but the kernel reports the sysexit
stage; just discard this spurious tracee/event. */
if (tracee->exe == NULL) {
tracee->restart_how = PTRACE_CONT;
return 0;
}
switch (tracee->seccomp) {
case ENABLED:
if (IS_IN_SYSENTER(tracee)) {
/* sysenter: ensure the sysexit
* stage will be hit under seccomp. */
tracee->restart_how = PTRACE_SYSCALL;
tracee->sysexit_pending = true;
}
else {
/* sysexit: the next sysenter
* will be notified by seccomp. */
tracee->restart_how = PTRACE_CONT;
tracee->sysexit_pending = false;
}
/* Fall through. */
case DISABLED:
if (!tracee->seccomp_already_handled_enter)
{
bool was_sysenter = IS_IN_SYSENTER(tracee);
translate_syscall(tracee);
/* In case we've changed on enter sysnum to PR_void,
* seccomp check is happening after our change
* and seccomp policy raises SIGSYS on -1 syscall,
* set tracee flag to ignore next SIGSYS. */
if (was_sysenter) {
tracee->skip_next_seccomp_signal = (
seccomp_after_ptrace_enter &&
get_sysnum(tracee, CURRENT) == PR_void);
}
/* Redeliver signal suppressed during
* syscall chain once it's finished. */
if (tracee->chain.suppressed_signal && tracee->chain.syscalls == NULL) {
signal = tracee->chain.suppressed_signal;
tracee->chain.suppressed_signal = 0;
VERBOSE(tracee, 6, "pid %d: redelivering suppressed signal %d", tracee->pid, signal);
}
}
else {
VERBOSE(tracee, 6, "skipping SIGTRAP for already handled sysenter");
assert(!IS_IN_SYSENTER(tracee));
assert(!seccomp_after_ptrace_enter);
tracee->seccomp_already_handled_enter = false;
tracee->restart_how = PTRACE_SYSCALL;
}
/* This syscall has disabled seccomp. */
if (tracee->seccomp == DISABLING) {
tracee->restart_how = PTRACE_SYSCALL;
tracee->seccomp = DISABLED;
}
break;
case DISABLING:
/* Seccomp was disabled by the
* previous syscall, but its sysenter
* stage was already handled. */
tracee->seccomp = DISABLED;
if (IS_IN_SYSENTER(tracee))
tracee->status = 1;
break;
}
break;
case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned long flags = 0;
signal = 0;
if (!seccomp_detected) {
tracee->seccomp = ENABLED;
seccomp_detected = true;
seccomp_after_ptrace_enter = !IS_IN_SYSENTER(tracee);
VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2, %s syscall order) enabled",
seccomp_after_ptrace_enter ? "new" : "old");
}
tracee->skip_next_seccomp_signal = false;
/* If kernel triggered seccomp event after we handled
* syscall enter, skip this event and continue as it didn't happen */
if (seccomp_after_ptrace_enter && !IS_IN_SYSENTER(tracee))
{
tracee->restart_how = tracee->last_restart_how;
VERBOSE(tracee, 6, "skipping PTRACE_EVENT_SECCOMP for already handled sysenter");
/* "!IS_IN_SYSENTER(tracee)" in condition above means we have pending sysexit,
* so requesting to not be notified about it makes no sense */
assert(tracee->restart_how != PTRACE_CONT);
break;
}
assert(IS_IN_SYSENTER(tracee));
/* Use the common ptrace flow if seccomp was
* explicitely disabled for this tracee. */
if (tracee->seccomp != ENABLED)
break;
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if (status < 0)
break;
/* Use the common ptrace flow when
* sysexit has to be handled. */
if ((flags & FILTER_SYSEXIT) != 0 || sysexit_necessary) {
if (seccomp_after_ptrace_enter) {
tracee->restart_how = PTRACE_SYSCALL;
translate_syscall(tracee);
}
tracee->restart_how = PTRACE_SYSCALL;
break;
}
/* Otherwise, handle the sysenter
* stage right now. */
tracee->restart_how = PTRACE_CONT;
translate_syscall(tracee);
/* This syscall has disabled seccomp, so move
* the ptrace flow back to the common path to
* ensure its sysexit will be handled. */
if (tracee->seccomp == DISABLING)
tracee->restart_how = PTRACE_SYSCALL;
if (!seccomp_after_ptrace_enter && tracee->restart_how == PTRACE_SYSCALL)
tracee->seccomp_already_handled_enter = true;
break;
}
case SIGTRAP | PTRACE_EVENT_VFORK << 8:
signal = 0;
(void) new_child(tracee, CLONE_VFORK);
break;
case SIGTRAP | PTRACE_EVENT_FORK << 8:
case SIGTRAP | PTRACE_EVENT_CLONE << 8:
signal = 0;
(void) new_child(tracee, 0);
break;
case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
case SIGTRAP | PTRACE_EVENT_EXEC << 8:
case SIGTRAP | PTRACE_EVENT_EXIT << 8:
signal = 0;
if (tracee->last_restart_how) {
tracee->restart_how = tracee->last_restart_how;
}
break;
case SIGSTOP:
/* Stop this tracee until PRoot has received
* the EVENT_*FORK|CLONE notification. */
if (tracee->exe == NULL) {
tracee->sigstop = SIGSTOP_PENDING;
signal = -1;
}
/* For each tracee, the first SIGSTOP
* is only used to notify the tracer. */
if (tracee->sigstop == SIGSTOP_IGNORED) {
tracee->sigstop = SIGSTOP_ALLOWED;
signal = 0;
}
break;
case SIGSYS: {
siginfo_t siginfo = {};
ptrace(PTRACE_GETSIGINFO, tracee->pid, NULL, &siginfo);
if (siginfo.si_code == SYS_SECCOMP) {
/* Signal cannot happen when we're inside syscall,
* tracee would have to exit from syscall first.
* Execute exit handler now if seccomp triggered sysexit skip. */
if (!IS_IN_SYSENTER(tracee)) {
VERBOSE(tracee, 1, "Handling syscall exit from SIGSYS");
translate_syscall(tracee);
}
if (tracee->skip_next_seccomp_signal || (seccomp_after_ptrace_enter && siginfo.si_syscall == SYSCALL_AVOIDER)) {
VERBOSE(tracee, 4, "suppressed SIGSYS after void syscall");
tracee->skip_next_seccomp_signal = false;
signal = 0;
} else {
signal = handle_seccomp_event(tracee);
}
} else {
VERBOSE(tracee, 1, "non-seccomp SIGSYS");
}
break;
}
default:
/* Deliver this signal as-is,
* unless we're chaining syscall. */
if (tracee->chain.syscalls != NULL) {
VERBOSE(tracee, 5,
"pid %d: suppressing signal during chain signal=%d, prev suppressed_signal=%d",
tracee->pid, signal, tracee->chain.suppressed_signal);
tracee->chain.suppressed_signal = signal;
signal = 0;
}
break;
}
}
/* Clear the pending event, if any. */
tracee->as_ptracee.event4.proot.pending = false;
return signal;
}
/**
* Returns true if on current system SIGTRAP|0x80
* for syscall enter is reported before SIGSYS
* when syscall is being blocked by seccomp
*/
bool seccomp_event_happens_after_enter_sigtrap()
{
return !seccomp_after_ptrace_enter;
}
/**
* Restart the given @tracee with the specified @signal. This
* function returns false if the tracee was not restarted (error or
* put in the "waiting for ptracee" state), otherwise true.
*/
bool restart_tracee(Tracee *tracee, int signal)
{
int status;
/* Put in the "stopped"/"waiting for ptracee" state?. */
if (tracee->as_ptracer.wait_pid != 0 || signal == -1)
return false;
/* Restart the tracee and stop it at the next instruction, or
* at the next entry or exit of a system call. */
assert(tracee->restart_how != 0);
status = ptrace(tracee->restart_how, tracee->pid, NULL, signal);
if (status < 0)
return false; /* The process likely died in a syscall. */
tracee->last_restart_how = tracee->restart_how;
tracee->restart_how = 0;
tracee->running = true;
return true;
}
+36
View File
@@ -0,0 +1,36 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef TRACEE_EVENT_H
#define TRACEE_EVENT_H
#include <stdbool.h>
#include "tracee/tracee.h"
extern int launch_process(Tracee *tracee, char *const argv[]);
extern int event_loop();
extern int handle_tracee_event(Tracee *tracee, int tracee_status);
extern bool restart_tracee(Tracee *tracee, int signal);
extern bool seccomp_event_happens_after_enter_sigtrap();
#endif /* TRACEE_EVENT_H */
Loaded 100 of 309 files, more files were not shown because too many files have changed in this diff. Show more