diff hook: execute as the build user, and pass the temp dir
This commit is contained in:
		
							parent
							
								
									c78686e411
								
							
						
					
					
						commit
						6df61db060
					
				
					 5 changed files with 51 additions and 28 deletions
				
			
		|  | @ -46,17 +46,15 @@ file containing: | ||||||
| #!/bin/sh | #!/bin/sh | ||||||
| exec >&2 | exec >&2 | ||||||
| echo "For derivation $3:" | echo "For derivation $3:" | ||||||
| /run/current-system/sw/bin/runuser -u nobody -- /run/current-system/sw/bin/diff -r "$1" "$2" | /run/current-system/sw/bin/diff -r "$1" "$2" | ||||||
| </programlisting> | </programlisting> | ||||||
| 
 | 
 | ||||||
| <warning> |  | ||||||
|   <para>The diff hook can be run as root. Take care to run as little |  | ||||||
|   as possible as root, for this example we use <command>runuser</command> |  | ||||||
|   to drop privileges. |  | ||||||
|   </para> |  | ||||||
| </warning> |  | ||||||
| </para> | </para> | ||||||
| 
 | 
 | ||||||
|  | <para>The diff hook is executed by the same user and group who ran the | ||||||
|  | build. However, the diff hook does not have write access to the store | ||||||
|  | path just built.</para> | ||||||
|  | 
 | ||||||
| <section> | <section> | ||||||
|   <title> |   <title> | ||||||
|     Spot-Checking Build Determinism |     Spot-Checking Build Determinism | ||||||
|  |  | ||||||
|  | @ -252,13 +252,11 @@ false</literal>.</para> | ||||||
|       same. |       same. | ||||||
|     </para> |     </para> | ||||||
| 
 | 
 | ||||||
|     <warning> |     <para> | ||||||
|       <para> |       The diff hook is executed by the same user and group who ran the | ||||||
|         The root user executes the diff hook in a daemonised |       build. However, the diff hook does not have write access to the | ||||||
|         installation. See <xref linkend="chap-diff-hook" /> for |       store path just built. | ||||||
|         information on using the diff hook safely. |     </para> | ||||||
|       </para> |  | ||||||
|     </warning> |  | ||||||
| 
 | 
 | ||||||
|     <para>The diff hook program receives three parameters:</para> |     <para>The diff hook program receives three parameters:</para> | ||||||
| 
 | 
 | ||||||
|  | @ -280,6 +278,14 @@ false</literal>.</para> | ||||||
|           The path to the build's derivation |           The path to the build's derivation | ||||||
|         </para> |         </para> | ||||||
|       </listitem> |       </listitem> | ||||||
|  | 
 | ||||||
|  |       <listitem> | ||||||
|  |         <para> | ||||||
|  |           The path to the build's scratch directory. This directory | ||||||
|  |           will exist only if the build was run with | ||||||
|  |           <option>--keep-failed</option>. | ||||||
|  |         </para> | ||||||
|  |       </listitem> | ||||||
|     </orderedlist> |     </orderedlist> | ||||||
| 
 | 
 | ||||||
|     <para>The diff hook should not print data to stderr or stdout, as |     <para>The diff hook should not print data to stderr or stdout, as | ||||||
|  |  | ||||||
|  | @ -461,17 +461,26 @@ static void commonChildInit(Pipe & logPipe) | ||||||
|     close(fdDevNull); |     close(fdDevNull); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void handleDiffHook(Path tryA, Path tryB, Path drvPath) | void handleDiffHook(bool allowVfork, uid_t uid, uid_t gid, Path tryA, Path tryB, Path drvPath, Path tmpDir) | ||||||
| { | { | ||||||
|     auto diffHook = settings.diffHook; |     auto diffHook = settings.diffHook; | ||||||
|     if (diffHook != "" && settings.runDiffHook) { |     if (diffHook != "" && settings.runDiffHook) { | ||||||
|         try { |         auto wrapper = [&]() { | ||||||
|             auto diff = runProgram(diffHook, true, {tryA, tryB, drvPath}); |             if (setgid(gid) == -1) | ||||||
|             if (diff != "") |                 throw SysError("setgid failed"); | ||||||
|                 printError(chomp(diff)); |             if (setuid(uid) == -1) | ||||||
|         } catch (Error & error) { |                 throw SysError("setuid failed"); | ||||||
|             printError("diff hook execution failed: %s", error.what()); | 
 | ||||||
|         } |             try { | ||||||
|  |                 auto diff = runProgram(diffHook, true, {tryA, tryB, drvPath, tmpDir}); | ||||||
|  |                 if (diff != "") | ||||||
|  |                     printError(chomp(diff)); | ||||||
|  |             } catch (Error & error) { | ||||||
|  |                 printError("diff hook execution failed: %s", error.what()); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         doFork(allowVfork, wrapper); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3197,14 +3206,18 @@ void DerivationGoal::registerOutputs() | ||||||
|             if (!worker.store.isValidPath(path)) continue; |             if (!worker.store.isValidPath(path)) continue; | ||||||
|             auto info = *worker.store.queryPathInfo(path); |             auto info = *worker.store.queryPathInfo(path); | ||||||
|             if (hash.first != info.narHash) { |             if (hash.first != info.narHash) { | ||||||
|                 handleDiffHook(path, actualPath, drvPath); |                 if (settings.runDiffHook || settings.keepFailed) { | ||||||
| 
 |  | ||||||
|                 if (settings.keepFailed) { |  | ||||||
|                     Path dst = worker.store.toRealPath(path + checkSuffix); |                     Path dst = worker.store.toRealPath(path + checkSuffix); | ||||||
|                     deletePath(dst); |                     deletePath(dst); | ||||||
|                     if (rename(actualPath.c_str(), dst.c_str())) |                     if (rename(actualPath.c_str(), dst.c_str())) | ||||||
|                         throw SysError(format("renaming '%1%' to '%2%'") % actualPath % dst); |                         throw SysError(format("renaming '%1%' to '%2%'") % actualPath % dst); | ||||||
| 
 | 
 | ||||||
|  |                     handleDiffHook( | ||||||
|  |                         !buildUser && !drv->isBuiltin(), | ||||||
|  |                         buildUser ? buildUser->getUID() : getuid(), | ||||||
|  |                         buildUser ? buildUser->getGID() : getgid(), | ||||||
|  |                         path, dst, drvPath, tmpDir); | ||||||
|  | 
 | ||||||
|                     throw Error(format("derivation '%1%' may not be deterministic: output '%2%' differs from '%3%'") |                     throw Error(format("derivation '%1%' may not be deterministic: output '%2%' differs from '%3%'") | ||||||
|                         % drvPath % path % dst); |                         % drvPath % path % dst); | ||||||
|                 } else |                 } else | ||||||
|  | @ -3269,7 +3282,11 @@ void DerivationGoal::registerOutputs() | ||||||
|                     ? fmt("output '%1%' of '%2%' differs from '%3%' from previous round", i->second.path, drvPath, prev) |                     ? fmt("output '%1%' of '%2%' differs from '%3%' from previous round", i->second.path, drvPath, prev) | ||||||
|                     : fmt("output '%1%' of '%2%' differs from previous round", i->second.path, drvPath); |                     : fmt("output '%1%' of '%2%' differs from previous round", i->second.path, drvPath); | ||||||
| 
 | 
 | ||||||
|                 handleDiffHook(prev, i->second.path, drvPath); |                 handleDiffHook( | ||||||
|  |                     !buildUser && !drv->isBuiltin(), | ||||||
|  |                     buildUser ? buildUser->getUID() : getuid(), | ||||||
|  |                     buildUser ? buildUser->getGID() : getgid(), | ||||||
|  |                     prev, i->second.path, drvPath, tmpDir); | ||||||
| 
 | 
 | ||||||
|                 if (settings.enforceDeterminism) |                 if (settings.enforceDeterminism) | ||||||
|                     throw NotDeterministic(msg); |                     throw NotDeterministic(msg); | ||||||
|  |  | ||||||
|  | @ -914,8 +914,8 @@ void killUser(uid_t uid) | ||||||
| 
 | 
 | ||||||
| /* Wrapper around vfork to prevent the child process from clobbering
 | /* Wrapper around vfork to prevent the child process from clobbering
 | ||||||
|    the caller's stack frame in the parent. */ |    the caller's stack frame in the parent. */ | ||||||
| static pid_t doFork(bool allowVfork, std::function<void()> fun) __attribute__((noinline)); | pid_t doFork(bool allowVfork, std::function<void()> fun) __attribute__((noinline)); | ||||||
| static pid_t doFork(bool allowVfork, std::function<void()> fun) | pid_t doFork(bool allowVfork, std::function<void()> fun) | ||||||
| { | { | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|     pid_t pid = allowVfork ? vfork() : fork(); |     pid_t pid = allowVfork ? vfork() : fork(); | ||||||
|  |  | ||||||
|  | @ -265,6 +265,8 @@ string runProgram(Path program, bool searchPath = false, | ||||||
|     const Strings & args = Strings(), |     const Strings & args = Strings(), | ||||||
|     const std::optional<std::string> & input = {}); |     const std::optional<std::string> & input = {}); | ||||||
| 
 | 
 | ||||||
|  | pid_t doFork(bool allowVfork, std::function<void()> fun); | ||||||
|  | 
 | ||||||
| struct RunOptions | struct RunOptions | ||||||
| { | { | ||||||
|     Path program; |     Path program; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue