WIP: fix(security): shared files, links and lists can't make the game, Builder or tools write or read where they shouldn't #218
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!218
Loading…
Reference in a new issue
No description provided.
Delete branch "TrashPanda/Longshot:security/hardening-passes"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Files, settings and lists people share or download (a
longshot_env.txtfrom a guide, a pipeline list someone sends, a game folder kept in OneDrive, a link left in the game folder) could make the game, the Builder or the tools write through a link, read past a file's end or take values nothing should have. This brings over the Hookshot branchsecurity/hardening-passes, which was never merged there before Hookshot was taken down, so Longshot gets the same checks.What changed
28 of the branch's 39 commits, each with its author, date and message (hckrman101 23, Joey Magnus 5), then 4 commits that fit them to Longshot and 4 that answer the first review. The other 11 are listed under Reviewer notes.
host/hos_fopen_write.h. Every file the game writes on its own is written without following a symbolic link or, on Windows, a junction at its name:longshot_options.ini,mouse_sens.txt,language.txt(from the first start and from the menu),longshot_log.txt, the pipeline caches and lists, the program cachelongshot_programs.binand its.idx(with the"r+b"that cuts it back to its last whole record) and its lock,longshot_module_ids.bin, the markers (load-default, its strike file, "preparation skipped", the pipeline stamp's.tmp), the bug report's files, and the button-prompt textures. A refused write is said once in the log.O_NOFOLLOW, thenfstaton the descriptor, and a regular file only.FILE_FLAG_OPEN_REPARSE_POINT) and refused when it is a name surrogate (IsReparseTagNameSurrogate: symbolic links, junctions, WSL's,WCI_LINK,GLOBAL_REPARSE). It is then opened for writing and kept only when it is the same file (volume and file index), so nothing can be swapped in between. OneDrive's files (not surrogates) are opened again as themselves, so one kept only online is filled before it is written. Paths go from UTF-8 toCreateFileW.user/) instead.host/hos_fopen_read.h, start-up files from the game folder are read only when they are regular files (the program cache and its index, the markers and the stamp now too); the button-prompt file only with its magic and under 256 MB; a pipeline cache file only after its header and not past 4 GB (kPcacheMax,gpu/vk/vk_draw.cpp).longshot_env.txtsets only the game's ownLONGSHOT_switches and no path-valued switch at all. That includesRX_VK_DLSS_DIR, which picks the folder NGX loads DLSS from; the game'sngxfolder and%LOCALAPPDATA%\Longshot\ngx/$XDG_DATA_HOME/longshot/ngxneed no switch. It also takes no dump or SDL file-writing switch, no frame trace, notHOS_PGO, and not the control bridge (env_line_allowed,host/hos_main.c). The environment and a fileHOS_ENV_FILEnames still set all of them.hos_guest_range: audio, HID, applets, NVN's proc names,oot_menu.c).targets_ok,streams_ok).nx2/pipeline_states.py'scheck_recordsrefuses records with values no pipeline has before a list is added, merged or shipped.refuse_written_links, also atlongshot_options.iniand.longshot_builder_home). For a folder it writes into (bin,romfs,prompts,ngx,ffx,.game,.longshot_exefs), the error names the folder and says to move the whole game folder to the other drive instead. What it reads back stays inside the folder. A folder OneDrive keeps still takes the known damaged-texture list (nup.extraction_path'slinks_only).deps.sh's checked download. The tools' Python packages are installed withpip --require-hashesfromrequirements-lock.txt, both the Linux AppImage's (deps.sh) and the Windows builder's (scripts/tools.py assemble_python).tests/test_workflow_hardening.pykeeps the workflows pinned, with no token left behind and no untrusted${{ }}in a script. It now reads.forgejo/workflowsand Forgejo's full-URL action names;third-party.yml'sgithub.base_refgoes throughenv:.tests/test_game_writes_no_links.pylooks through every source underhost/andgpu/vk/(nothost/posixor the vendored code) for opens that write. Each must go throughhos_fopen_write, make its file new ("x",O_EXCL,O_NOFOLLOW,CREATE_NEW), or be in a reviewed list. That list holds the switch-gated dumps and traces, each with its switch and reason (a switch the shared file cannot set, checked), and four writes that name no file of the game folder: the save file system, Discord's pipe, the console and udmabuf. A new cache file written with a plainfopenfails it.Fitting the branch to Longshot (the 4 commits after the port, and conflict notes in the ported ones):
pipeline_states.py: Longshot'sread_sentand its xz limit kept.check_recordsruns besidecheck_kinds, and it skips compute-program records (a key and zeros), whichcheck_kindsalready holds to exactly that.nup.py: Longshot'spackage_parts,is_linkandDestinationkept;is_linktakeslinks_only.test_workflow_hardening.py:release.ymlandwebsite.ymlhave no top-levelpermissions:(Forgejo 17 ignores the key), andpr-description.ymlandwebsite.ymlcheck out underpull_request_target(main's script only, and a fork's commit only after a maintainer'spreviewlabel). The check lists these as reviewed, with the reasons.test_linux_saves_move,test_mouse_sens_file,test_prepare_stall,test_mesh_mode_switch,test_predict_resume,test_env_file,test_pipeline_state_computes, and after the rebasetest_udmabuf_warningandtest_upscaler_round_trip);test_pipeline_share's records are ones the game can make.Flags and settings
longshot_env.txtRX_VK_DLSS_DIRincluded. Also notRX_SCREENSHOT,RX_FLASH_LOG,HOS_SCAN_DT(dumps),RX_VK_FRAME_TRACE,HOS_PGO, SDL's file-writing switches,HOS_CONTROLorHOS_CONTROL_MEM. Of theLONGSHOT_names it takes only the game's own,LONGSHOT_NO_AV1_NOTICEandLONGSHOT_OCCLUSION_OFF. No switch is new; set in the environment, or in a fileHOS_ENV_FILEnames, each works as before.Measurements
main:
d906c8f4(2026-11-13)this PR: security/hardening-passes @
46ec4bd7No speed numbers: checks on reads, writes and shared files, off the frame path. The game-free checks on both sides:
d906c8f446ec4bd7python -I -B tests/run_all.py --jobs 4failures (Linux, this PC)test_lift_cache(no numpy here),test_menu_i18n(since #138)longshot_vk.dll(RX_TARGET=windows LONGSHOT_MINGW=<llvm-mingw> RX_VK_ONLY=1 nx2/build.py --standalone, on Linux)longshot_vk.soobjects (RX_VK_ONLY=1 nx2/build.py --standalone, Linux)lld, which this PC lacksTesting
Tested: on Linux (RTX 4070 Ti, driver 595.99.02, Ryzen; Python 3.14), without the game:
python -I -B tests/run_all.py --jobs 4: 508 passed, 2 failed, 30 skipped. Both failures also fail onmainhere:test_lift_cache(no numpy on this PC) andtest_menu_i18n(pt_BR's text for a row #138 changed).tests/test_game_writes_no_links.pyfails on83e9a080(the head the review read) with every write the review named, plus the log, the bug report, the program cache's lock and the probe and draw-stats files, and passes here.tests/test_env_file.pyrefusesRX_VK_DLSS_DIRfrom the shared file for every value (/tmp/x,/dev/shm/x,C:\Users\Public\...,Z:\...,/proc/self/cwdand the rest), and takes it fromHOS_ENV_FILE's file and the environment.hos_fopen_write.hbuilt against llvm-mingw's real Windows headers, as C and as C++ (LONGSHOT_MINGW=... tests/test_game_writes_no_links.py). The Windows rendererlongshot_vk.dllbuilds and links. The Windows branch's behaviour (surrogates refused before any write open, OneDrive tags written, a name swapped between the two opens refused,"r+b"not creating) runs against a stand-inwindows.h.pip install --require-hashesofnx2/prompt_requirements.txtforwin_amd64from the lock, against PyPI: installs.Not tested: the game. The host C (
host/*.c) built whole needs the lifted game's headers (a64.h), which only a build from a dump makes; the checks that cut and compile host functions pass. Windows: nothing ran on Windows itself (no Windows PC or Wine here), soCreateFileW/_open_osfhandleon a real NTFS junction, symlink or OneDrive placeholder is untested. The two Windows requests wait for thetesting wantedlabel before anyone can claim them.Needs other testers:
longshot_options.iniand atlongshot_programs.bin, expecting the files they point at to be left alone and one log line each (T-AJTA)Screenshots
Nothing on screen
Risk and rollback
A check that is too strict would refuse a file the game needs: options or a save not written, a pipeline or program cache not read or kept, the log not opened, or a
longshot_env.txtswitch that someone relied on ignored (RX_VK_DLSS_DIRthere now is: it goes in the environment, such as Steam's launch options). Each refusal says so inlongshot_log.txt, but a refused log falls back to where stderr went before. PRs are squash-merged, so reverting this PR reverts all of it; the parts are in separate commits here for review, not for separate reverts onmain.Reviewer notes
Ported with
work/port_to_longshot.py(renameshookshottolongshot,.github/to.forgejo/), as #209 was. Commit messages are kept as written, so some still sayhookshot_env.txt. Where to look first:host/hos_fopen_write.h,host/hos_fopen_read.h,env_line_allowedinhost/hos_main.c,REVIEWEDintests/test_game_writes_no_links.py, andnx2/pipeline_states.py.This is several topics in one PR (link-safe writes and the Builder, the env file, HLE guest ranges, pipeline-list validation, the supply chain). It can be split along those lines; see the answer to the review.
REVIEWEDkeeps the save file system'sOpenFile(host/nn_fs.c, the game's saves inuser/, opened again after the game made them) as a write that follows a link: it is not a name the game folder ships, and making it no-follow needs the Linux stand-inCreateFileA(host/posix) to takeFILE_FLAG_OPEN_REPARSE_POINT. A follow-up if wanted.Not ported (11):
875b8bdb,7a56ee64,70b495de: the game's update check and its skip file, removed in #13.1a7dd521: the Python builder, removed in #181.c03691a7,8f029bff,c5fdeb2b: the WPF builder's release-name and download rules (files Longshot removed).b7fa0d7b:release_rc.sh(removed).6bb6763e: Dependabot (GitHub only).54dba137: Longshot already copies a link inuser/as a link and deletes only the link (copy_link,delete_tree).8d23c0a0"setup lists' parts come only from this project's releases": needs a port of its own. Longshot's setup fetches the launcher andtools.lock's parts from Forgejo and its mirrors, so the GitHub host allowlist doesn't carry over; a follow-up.Issues
See #209
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.The game rewrites hookshot_options.ini when an option changes and writes its pipeline caches, state lists and their markers, the button-prompt textures, mouse_sens.txt, language.txt and the moved-saves note by itself. A game folder unpacked from an archive someone shared can hold symbolic links (tar and unzip keep them on Linux), and a plain fopen("w") through one emptied and rewrote the file it named. host/hos_fopen_write.h opens these with O_NOFOLLOW, only as a regular file (not a FIFO or a device), and empties it only once that is known. Windows is unchanged: an archive's links are not made there without a privilege. The program cache (gpu/vk/vk_shader.cpp) is left for later. New tests/test_game_writes_no_links.py: the helper's behaviour, and every write in those files goes through it but the switch-gated debugging ones. The checks that cut those writers out of vk_draw.cpp now build them with the helper. Co-Authored-By: Claude Opus 5.5 <[email protected]> Ported to Longshot: the pipeline caches are written by Longshot's PipelineCacheFile (a new mkstemp file, then a rename), which never writes through a link at the name, so those two writers stay as they are. Longshot's own markers and lists in vk_draw.cpp (the driver fault note, the precompile marker, the compute variants, the warm and predict markers, the predicted lists) take hos_fopen_write too, and the two checks that cut them include it.The game reads hookshot_env.txt, hookshot_options.ini, language.txt and mouse_sens.txt from its folder at the start. A folder unpacked from an archive can hold a FIFO under one of those names (tar keeps them), and fopen("r") on it waited forever, before the window opened, at every start. New host/hos_fopen_read.h has hos_fopen_read: on Linux it opens without waiting and takes only a regular file (a link to one is fine); on Windows it is fopen. The reads of those files go through it, and hos_fopen_read_binary takes the renderer's reads of its caches, notes, state lists, preparation records and markers (gpu/vk/vk_draw.cpp) and the button-prompt files (host/prompts.c) the same way, as do the renderer's start-up reads of hookshot_options.ini and its skip marker (gpu/vk/vk_main.cpp) and of the game's font files (vk_game_font.h). On Linux, the copy that moves an old user/ folder to the saves folder reads through it too, so a FIFO there fails the copy (the entry stays) instead of holding the start. The helpers live in host/hos_fopen_read.h, which needs no windows.h, so the checks that cut a reader out of the sources still build on Windows beside their own stand-ins; hos_fopen_write.h includes it, and the renderer's object keys take it in. tests/test_game_writes_no_links.py checks the helper (a FIFO and a folder refused at once, a file and a link to one read) and that each read goes through it; the checks that cut those readers out include the header. Co-Authored-By: Claude Opus 5.5 <[email protected]> Ported to Longshot: Longshot's pipeline caches are read by PipelineCacheFile::read (a regular file only too), and Longshot's own start-up reads in vk_draw.cpp (the driver faults, compute variants, state lists, warm and predict markers, predicted lists, the precompile marker and the notes) go through hos_fopen_read too. Also gives tests/test_pipeline_state_computes.py the device limits b59187f5's sample-count check reads.Read through the PR-only diff (dd02c6cc..83e9a080) and the code around it: the callers of the two new headers,
env_line_allowedwith the code that reads each switch, the Builder's link checks,nup.py, and the HLE guest-range changes. I built and ran nothing.What's good
hos_fopen_writehas no race at the file's name:O_NOFOLLOW | O_NONBLOCK, thenfstaton the descriptor, thenftruncateonly once it knows the file is regular. The read side refuses FIFOs the same way. A symlinkeduser/(the saves move) and a symlinked game folder still work, because only the last part of the path is checked.env_line_allowedis a real improvement. Every*_DUMPswitch is blocked as a family, and so areHOS_CONTROL/HOS_CONTROL_MEM.LONGSHOT_names are narrowed to the game's two, andRX_VK_FSR_DIRand the other_DIR/_FILEnames are refused. The source scan intest_env_file.pywill catch most new path switches.links_onlykeeps a game folder that OneDrive manages working (its placeholders aren't links), whilerefuse_dump_linksstays strict.stand_in_damagednow copies into anmkstempfile and no longer writes to a fixed.partname.hos_guest_rangechecks are careful:nvnBootstrapLoadercaps a name at the end of guest memory, the wave buffers are checked once and that checked copy is the one submitted, andnpad_stateslimits its count.Must change
Several files the game writes on its own still use a plain
fopen. The description says the game's own files, "the pipeline caches, the markers", are no longer written through a link, and the header comment nameslanguage.txt. At83e9a080these still write through a link at their name:gpu/vk/vk_progcache.cpp:120,124,251,253,255,265:longshot_programs.binand.idx, with"wb","ab"and"r+b"plus_chsize_s. This is the largest file the game writes, and it is written on every first start.gpu/vk/vk_menu_i18n.cpp:226:language.txtwhen the language row changes. Only the first write, innn_applet.c, was converted.gpu/vk/load_default.h:69,79,92: the load-fallback marker and its strike file.gpu/vk/module_ids.h:83:longshot_module_ids.bin.gpu/vk/vk_main.cpp:1090: the "preparation skipped" marker. The PR converted its read but not this write.gpu/vk/pcache_stamp.h:38:mkw_vk_pipelines.stamp.tmp. A link there is written through before the rename.tests/test_game_writes_no_links.pymisses all of them becauseALLOWEDlists only six files. Convert these writes, and have the check scan every file underhost/andgpu/vk/with a list of reviewed switch-gated writes, not the other way round. Otherwise the next new cache file is back to the old behaviour.RX_VK_DLSS_DIRis still a library-load path that a shared file can set. It is the onelongshot_env.txtswitch left that names a path, and it decides where NGX loadsnvngx_dlss.dllorlibnvidia-ngx-dlss.sofrom. NGX takes the newest runtime it finds, so a planted copy wins over the game folder's./tmp/x,/dev/shm/x,C:\Users\Public\..., a mapped network drive (Z:\) and/proc/self/cwd. So a shared file, plus anything that can drop a file into a world-writable folder, loads a library.<game>/ngxand$XDG_DATA_HOME/longshot/ngx(%LOCALAPPDATA%\Longshot\ngx). The simplest fix is to refuse this switch from the shared file too, likeRX_VK_FSR_DIR;HOS_ENV_FILEstill sets it. If it stays, at least refuse a folder the player doesn't own or that others can write to (on Linux,statit: uid, and noS_IWGRP|S_IWOTH), and refuse non-fixed drives on Windows (GetDriveTypeW).The "Rebased on current
main" box is ticked but isn't true.dd02c6cc.mainis1ef93e79, four commits ahead (#54, #78, #147, #197). Three of them touch files this PR changes:host/hos_main.c,gpu/vk/vk_draw.cppandgpu/vk/vk_main.cpp.tests/run_all.py: #147'stest_dev_packs.pyand #197'stest_vk_occlusion_queries.pycut code from files this PR changes.Should change
hos_fopen_write(inline comment):IsReparseTagNameSurrogate(tag), not three fixed tags. That also coversWCI_LINKandGLOBAL_REPARSE, and still lets OneDrive's cloud tags through.CreateFileW(..., FILE_FLAG_OPEN_REPARSE_POINT), checkGetFileInformationByHandleEx(FileAttributeTagInfo)on the handle, and only then_open_osfhandle/_fdopen. That closes the window betweenGetFileAttributesAandfopen, the same way the Linux side has none. Low urgency: the threat here is files sitting in an archive, not a race.python_lock, the SDK downloads,deps.sh, the workflow check). PRs are squash-merged (CONTRIBUTING.md), so the description's "a single commit can be reverted on its own" won't hold onmain. A regression in one part, such as a player's options not saving, would mean reverting all 2,086 lines.romfs,bin,prompts,ngx,ffxor.game(FOLDER_WRITES). That's right for safety. But the README change tells players to "link the folder that holds them, not the file", and a Steam Deck player who pointsromfsat the SD card now gets a hard stop. Say in the README which folders the Builder won't take as links. The error could also name the folder to move instead.--require-hashescoversdeps.shonly. The Windows tools Python (scripts/tools.py assemble_python,nx2/prompt_requirements.txt) still installs unpinned, so the "the shipped Python's packages" line should say Linux. Or use the lock there too: its header says it lists the Windows x64 wheels too.Not checked: I didn't build or run anything, Windows or Linux. I couldn't confirm whether NGX checks the runtime's signature on Linux. I didn't follow every guest-range change into the game's real call sites (for example, whether OoT ever asks
GetNpadStatesfor more than 16 states).@ -0,0 +1,65 @@/* (longshot) fopen for the files the game writes by itself in its folder and the user folder (longshot_options.ini, theThe comment names
language.txt, the pipeline caches and the markers, but some of the game's own writes still use a plainfopen.vk_menu_i18n.cpp:226writeslanguage.txt(the menu's write).vk_progcache.cppwriteslongshot_programs.bin/.idxwithwb,abandr+b. Also still plain:load_default.h(the markers),module_ids.h:83,vk_main.cpp:1090(the skipped marker) andpcache_stamp.h:38(.tmp). Each of these writes through a link at its name.@ -0,0 +34,4 @@const HANDLE h = FindFirstFileA(path, &fd);if (h == INVALID_HANDLE_VALUE) return NULL;FindClose(h);if (fd.dwReserved0 == 0xA000000CUL || fd.dwReserved0 == 0xA0000003UL || fd.dwReserved0 == 0xA000001DUL) {Two suggestions. First,
IsReparseTagNameSurrogate(fd.dwReserved0)covers every link-like tag (WCI_LINK,GLOBAL_REPARSE, any added later) and still lets OneDrive's cloud tags through, which aren't surrogates. Second, the check and thefopenare two lookups of the name.CreateFileW(..., FILE_FLAG_OPEN_REPARSE_POINT)followed byGetFileInformationByHandleEx(FileAttributeTagInfo)and_open_osfhandlewould check the very file it then writes, the way the Linux side already does.@ -458,0 +472,4 @@/* RX_VK_DLSS_DIR: a folder on this PC named in full (C:\x on Windows, /x on Linux), not a share (\\server\x or//server/x) or a device path; (longshot) not a relative one either, which NGX resolved against whatever folderthe game was started from, and no .. step out of the folder the value names */if (!strcmp(name, "RX_VK_DLSS_DIR")) {This is the last switch a shared file can point at a path, and it picks which library NGX loads (it takes the newest runtime it finds). A full path still accepts
/tmp/x,/dev/shm/x,C:\Users\Public\...and a mapped network drive (Z:\). The default search already covers<game>/ngxand$XDG_DATA_HOME/longshot/ngx. I'd refuse it here likeRX_VK_FSR_DIRand leave it toHOS_ENV_FILE. If it stays, refuse a folder that is group- or world-writable or that the player doesn't own (Linux), and refuse anything but a fixed drive (Windows).@ -111,0 +111,4 @@# (longshot) what this builder writes at the top of a game folder (and in its bin): a link at one of these names, which a folder# from elsewhere can hold (an update builds into the folder as it is), was written through, and the file or folder it named# rewritten. tests/test_game_folder_links.py checks the list against the names this file joins to the folderFOLDER_WRITES = ["bin", "image.bin", "longshot_version.txt", "longshot_options.ini", "language.txt", "longshot.png", "longshot_pipelines.bin",romfs,bin,prompts,ngxandffxare folders, so the Builder now stops when any of them is a link. A Steam Deck player who pointsromfsat the SD card gets a hard stop, while the README's new note says to "link the folder that holds them". Please say in the README which folders mustn't be links, and have the error name the folder to move.@ -0,0 +35,4 @@# (the sources of the writes listed above, and how many plain fopen writes each keeps: switch-gated debugging only.# vk_draw.cpp: RX_VK_PROBE's two files and RX_VK_DRAW_STATS' file)ALLOWED = {"gpu/vk/vk_menu.cpp": 0, "gpu/vk/vk_draw.cpp": 3, "host/nn_applet.c": 0, "host/prompts.c": 0,ALLOWEDlists six files, so this check doesn't see the plain writes invk_progcache.cpp,vk_menu_i18n.cpp,load_default.h,module_ids.h,pcache_stamp.h, or the write invk_main.cpp. Scan every file underhost/andgpu/vk/instead, and list the reviewed switch-gated writes, so a new cache file fails here.83e9a080aff7b79b8986f7b79b8986641bf324a8Thanks for the careful read. The branch is rebased on
main@d906c8f4(it had moved past1ef93e79), with 4 new commits on top; the head is46ec4bd7. (Edited:mainmoved during the work, so I rebased once more; the hashes here are the current ones.) Point by point:Must change
Done in
da41af87.vk_progcache.cpp(.bin/.idxwith"wb","ab", and"r+b"+_chsize_s, whichhos_fopen_writenow takes as an existing-file-only mode),vk_menu_i18n.cpp'slanguage.txt,load_default.h's marker and strike file,module_ids.h,vk_main.cpp's skipped marker andpcache_stamp.h's.tmp.longshot_log.txt(thefreopenonto stderr, nowhos_freopen_write), the bug report's copied logs andreport.txt, the program cache's.lock(O_NOFOLLOW), andRX_VK_PROBE's andRX_VK_DRAW_STATS' files, whose switches a sharedlongshot_env.txtcan set.language.txt) go throughhos_fopen_read*too, so a FIFO there isn't waited on.tests/test_game_writes_no_links.pyis inverted. It walks every source underhost/andgpu/vk/(nothost/posix,eden,sirit,compatorgpu/vk/tests), strips comments, and finds every open that writes:fopen/freopenwith a mode that writes,openwithO_WRONLY/O_RDWR/O_CREAT/O_APPEND,CreateFile*with write access,ofstream,CopyFile/copy_file."x",O_EXCL,O_NOFOLLOW,CREATE_NEW), or be inREVIEWED. EachREVIEWEDentry names its file, its call, its switch and a reason.REVIEWEDswitch is compiled throughenv_line_allowedand must be refused from the shared file. That's howHOS_PGOturned up; it's refused now, ina1463821.nn_fs.c's saveOpenFile, Discord's pipe, the console handles, and/dev/udmabuf. The save file system is the one I'd flag. Making it no-follow means teaching the LinuxCreateFileAstand-inFILE_FLAG_OPEN_REPARSE_POINT; I left it as a reviewed exception, open to a follow-up.83e9a080, the new check fails on all of the above.Done in
a1463821, the simple way you suggested. Its special case inenv_line_allowedis gone, so it falls under the_DIRrule. The environment (Steam launch options, a shell) and a fileHOS_ENV_FILEnames still set it, sinceenv_line_allowedonly applies to the shared file.test_env_file.pynow checks all of that:longshot_env.txt, including/tmp/x,/dev/shm/x,/proc/self/cwd,C:\Users\Public\ngxandZ:\ngx;HOS_ENV_FILE's file sets it;The README's DLSS and switches sections say where to set it now. With the switch refused, the Linux NGX signature question no longer decides anything.
Rebased on
d906c8f4, which includes #54, #78, #147, #197 and the 17 merged since. The rebase was clean. Three checks that cut code this PR changes needed updates:test_udmabuf_warning.py: the log'sfreopenbecamehos_freopen_write.test_upscaler_round_trip.py: the options file is read withhos_fopen_read, and the fixture includes the helper.test_prepare_stall.py: the watchdog's skipped marker is written withhos_fopen_write.test_dev_packs.pyandtest_vk_occlusion_queries.pypass.tests/run_all.py --jobs 4: 508 passed, 2 failed, 30 skipped. Both failures also fail onmainon this PC:test_lift_cache(no numpy) andtest_menu_i18n(since #138). "One topic" is unticked, with a note.Should change
Done in
da41af87, both parts, with one change to the second:FILE_READ_ATTRIBUTESandFILE_FLAG_OPEN_REPARSE_POINTonly, and its tag is checked on that handle withIsReparseTagNameSurrogate.GetFileInformationByHandlegives the same volume and file index. After that comeSetEndOfFilefor"w", then_open_osfhandle(_O_APPEND/_O_TEXTas the mode says) and_fdopen.FILE_FLAG_OPEN_REPARSE_POINTwould skip hydration. The file-index match closes the window you pointed at all the same.CREATE_NEW, which fails if a link appears in between.MB_ERR_INVALID_CHARS, aspipeline_cache_file.hdoes.The stand-in-
windows.hfixture now checks that symlink, junction, LX,WCI_LINK_1andGLOBAL_REPARSEare refused before any write-access open. It also checks that two cloud tags are written, a swap between the two opens is refused (and both handles closed), and"r+b"doesn't create. WithLONGSHOT_MINGWset, the check also builds the header as C and C++ against llvm-mingw's real headers. The whole Windowslongshot_vk.dllbuilds and links with llvm-mingw, without warnings. Nothing has run on Windows itself yet: T-AJTA asks for a real junction and symlink test.Not done in this round. Answering the review on one branch kept the thread readable, and the must-changes cut across the parts: the env file's refusals now back the write check's reviewed list. The split I'd make:
python_lock, SDK downloads,deps.sh, the workflow check).Say if you want it before merge and I'll split along those lines; the commits already fall that way. Meanwhile the description no longer claims a single commit can be reverted on
main.Done in
0f1e28ed.FOLDERS_WRITTENlists the folders the Builder writes into:bin,romfs,prompts,ngx,ffx,.gameand.longshot_exefs. For a link at one of them the error now names the folder and says to move the whole game folder to the other drive (an SD card) and build into it, or else to remove the link. The README's "Files at a link" lists the same folders and says a link to the game folder itself, or touser/, is fine.test_game_folder_links.pychecks the message and that every folder is named in the README.Done in
46ec4bd7, using the lock.assemble_pythonwrites the missing pins withrequirements-lock.txt's SHA-256s (they already include thewin_amd64wheels) and runs pip with--require-hashes. A pin that isn't in the lock at its version stops it before pip runs. I ran the real command fornx2/prompt_requirements.txtagainst PyPI (--platform win_amd64) and it installs.python_lock.py's docstring and the description now say both Pythons are covered.Line comments
host/hos_fopen_write.h(the header comment names writes still on plainfopen): converted,da41af87. The comment now lists the program cache, the markers and the log too.host/hos_fopen_write.h(three fixed tags; two lookups of the name):IsReparseTagNameSurrogateand a handle-based check,da41af87, as above.host/hos_main.c(RX_VK_DLSS_DIR): refused from the shared file,a1463821.tests/test_game_writes_no_links.py(ALLOWEDlists six files): replaced by the full scan withREVIEWED,da41af87.nx2/make_game.py(FOLDER_WRITESstops a linkedromfs): README list and an error that names the folder,0f1e28ed.Your "not checked" items
GetNpadStates' count into the game's own calls either: no change there in this round.Testing
Nothing ran in the game, so the PR stays WIP. Two Windows test requests are open:
longshot_options.iniandlongshot_programs.bin.Neither can be claimed until a maintainer adds
testing wanted. A Linux build and play from a dump is still to request.641bf324a846ec4bd792Rebased once more onto main @
d906c8f4(eight PRs merged while I worked); head46ec4bd7, same changes. The answer above now has the current hashes. run_all: 508 passed, 2 failed (test_lift_cache and test_menu_i18n, both also failing on main here); longshot_vk.dll builds and links again with llvm-mingw.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.