Skip to content

Commit

Permalink
Add abiCompiblePatches layer.
Browse files Browse the repository at this point in the history
  • Loading branch information
nbp committed Nov 6, 2015
1 parent d30dc0f commit 1b2e44e
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 2 deletions.
3 changes: 3 additions & 0 deletions lib/customisation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ rec {
{ override = newArgs: makeOverridable f (overrideWith newArgs);
overrideDerivation = fdrv:
makeOverridable (args: overrideDerivation (f args) fdrv) origArgs;
originalArgs = origArgs;
})
else if builtins.isFunction ff then
{ override = newArgs: makeOverridable f (overrideWith newArgs);
__functor = self: ff;
overrideDerivation = throw "overrideDerivation not yet supported for functors";
originalArgs = origArgs;
}
else ff;

Expand Down Expand Up @@ -108,6 +110,7 @@ rec {
pkgs = f finalArgs;
mkAttrOverridable = name: pkg: pkg // {
override = newArgs: mkAttrOverridable name (f (finalArgs // newArgs)).${name};
originalArgs = finalArgs;
};
in lib.mapAttrs mkAttrOverridable pkgs;

Expand Down
3 changes: 2 additions & 1 deletion pkgs/stdenv/linux/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ rec {
thisPkgs = allPackages {
inherit system platform;
bootStdenv = thisStdenv;
useQuickfix = false;
};

in { stdenv = thisStdenv; pkgs = thisPkgs; };
Expand Down Expand Up @@ -307,7 +308,7 @@ rec {


testBootstrapTools = let
defaultPkgs = allPackages { inherit system platform; };
defaultPkgs = allPackages { inherit system platform; useQuickfix = false; };
in derivation {
name = "test-bootstrap-tools";
inherit system;
Expand Down
135 changes: 134 additions & 1 deletion pkgs/top-level/all-packages-wrapper.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

, crossSystem ? null
, platform ? null

# This should be set to false when used by stdenv functions, which in the
# end should not rely imports of this file.
, useQuickfix ? true

This comment has been minimized.

Copy link
@Mathnerd314

Mathnerd314 Nov 7, 2015

I don't think this is necessary, you can just check if bootStdenv == null, like in configOverrides.

But I do agree that bootStdenv should go away; filed NixOS#10874.

This comment has been minimized.

Copy link
@nbp

nbp Nov 19, 2015

Author Owner

Good call, but I will keep this flag for testing at the moment. I will remove this one in the final version.

Note that we need another one for Hydra, such that hydra can stop its evaluation after the quickfix step while the user would want to go beyond the quickfix part.

}:


Expand Down Expand Up @@ -105,6 +109,135 @@ let
then self: super: config.packageOverrides super
else self: super: {};

# Apply ABI compatible fixes, recompile every program with the
# dependencies from the super set only and apply patches on the remaining
# packages. This will recompile only programs which have changed in the
# quickfix.
abiCompatiblePatches = pkgs:
if useQuickfix && builtins.pathExists ../../quickfix/pkgs/top-level/all-packages.nix then with lib;
# assert builtins.trace "!!! Apply abiCompatiblePatches !!!" true;
let
# Evaluate the set of packages from the quickfix index, with the
# list of fixed-point packages. These packages are unlikely to be
# too different than the original list of packages, thus these
# expressions should lead most of the time the same result as the
# fixed point.
quickfixPkgs = import ../../quickfix/pkgs/top-level/all-packages.nix;
quickFixPkgsFun = deps:
let
pkgs_ = quickfixPkgs {
self = deps; # Do no recurse here.

This comment has been minimized.

Copy link
@Mathnerd314

Mathnerd314 Nov 7, 2015

It doesn't seem to harm anything to use pkgs_ here, and I think using deps causes an infinite recursion that gets caught by validDeps. I can't prove it though.

This comment has been minimized.

Copy link
@nbp

nbp Nov 19, 2015

Author Owner

Thanks, I replaced it by the actual function used in the fix-point, as this fit better with the ideal model that I presented in my talk.

pkgs = deps;
defaultScope = deps // deps.xorg; # Do not recurse here.
inherit system crossSystem platform bootStdenv noSysDirs
gccWithCC gccWithProfiling config;
};
in pkgs_.stdenvAdapters // pkgs_.trivial-builders // pkgs_;

patchDependencies = drv: hashesMap: pkgs.runCommand "quickfix-${drv.name}" { nixStore = "${pkgs.nix}/bin/nix-store"; } ''
$nixStore --dump ${drv} | sed 's|${baseNameOf drv}|'$(basename $out)'|g;${
concatStrings (mapAttrsToList (name: value:
"'s|${baseNameOf name}|${baseNameOf value}|g';"
) hashesMap)
} | $nixStore --restore $out
'';

# For each package, we check if we have the same dependencies.
quickFixAsPatches = name: pkgs: quickfix: whatif:

This comment has been minimized.

Copy link
@Mathnerd314

Mathnerd314 Nov 7, 2015

s/pkgs/pkg/ (it's only one...)

let
# Note, we need to check the drv.outPath to add some strictness
# to eliminate derivation which might assert when they are
# evaluated.
validDeps = name: drv:
let res = builtins.tryEval (isDerivation drv && isString drv.outPath); in
name != "_currentPackage" && res.success && res.value;

This comment has been minimized.

Copy link
@Mathnerd314

Mathnerd314 Nov 7, 2015

I guess this is a hack until the promised "Optimize this function by only using runtime dependencies of the original package set" is implemented? There's some existing work in https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/replace-dependency.nix if you haven't seen it.


differentDeps = x:
# assert __trace "differentDeps: ${x.name}\n : : ${x.value} ?" true;
x.name != x.value;

# Based on the derivation, get the list of dependencies.
#
# :TODO: Optimize this function by only using runtime
# dependencies of the original package set, and which are
# computed ahead by the buildfamr.
getDeps = drv:
if drv ? originalArgs then filterAttrs validDeps drv.originalArgs
else assert __trace "Security issue: Unable to locate dependencies of `${name}`." true; {};

# This assumes that the originalArgs list are ordered the same
# way, as they are both infered from the same files.
hashesAssocList =
let qDeps = getDeps quickfix; wDeps = getDeps whatif; in
let names = attrNames qDeps; in
# assert __trace "qDeps: ${toString (attrNames qDeps)}\ntrace: wDeps: ${toString (attrNames wDeps)}" true;
assert names == attrNames wDeps;
filter differentDeps (map (name: {
name = builtins.unsafeDiscardStringContext (toString qDeps.${name});
value = toString wDeps.${name};
}) names);

# If the name of the quickfix does not have the same
# length, use the old name instead. This might cause a
# problem if people do not use --leq while updating.
quickfixRenamed =
if stringLength pkgs.name == stringLength quickfix.name
then quickfix
else
overrideDerivation quickfix ({
name = pkgs.name;
});
in
if length hashesAssocList != 0 then
# One of the dependency is different.
#throw "Is about to patch ${name}, because of ${showVal (listToAttrs hashesAssocList)}."
patchDependencies quickfixRenamed (listToAttrs hashesAssocList)
else
quickfixRenamed;

# Recursively decent into all packages until we reach a derivation,
# in which case we execute the "f" function, otherwise, if we cannot
# decide, such as in case of functions, then we execute the
# "default" function with both arguments.
zipQuickFixAsPatches = path: pkgs: quickfix: whatif:
zipAttrsWith (name: values:
# Somebody added / removed a packaged in quickfix?
let pkgsName = concatStringsSep "." (path ++[name]); in
# assert builtins.trace "zipQuickFixAsPatches (name: ${pkgsName})" true;
assert builtins.length values == 3;
let p = head values; q = head (tail values); w = head (tail (tail values)); in
if name == "pkgs" then q # We should not recurse in the top-level pkgs argument.
else if isAttrs p then
assert isAttrs q && isAttrs w; # Do not mutate the derivation
if isDerivation p then
assert isDerivation q && isDerivation w;
addErrorContext "While evaluating package ${pkgsName}" (quickFixAsPatches pkgsName p q w)
else
zipQuickFixAsPatches (path ++ [name]) p q w
else
q
) [pkgs quickfix whatif];

# Pipeline of modification involved to apply security patches:
#
# 1. We apply security patches on top of the current set of packages.
#
# 2. We check what package would be recompiled, if we were to
# recompile instead of applying patches.
#
# 3. We only keep the set of packages where we only applied patches.
#
quickFix = quickFixPkgsFun pkgs;
whatIf = quickFixPkgsFun abiSec;
abiSec = zipQuickFixAsPatches ["pkgs"] pkgs quickFix whatIf;
in
abiSec
else
# If there is no quickfix to apply, then there is no need for extra
# overhead, in which case we just the fix-point of packages.
pkgs;


# The package compositions. Yes, this isn't properly indented.
pkgsFun = pkgs:
let
Expand All @@ -125,4 +258,4 @@ let

in lib.mapAttrs tweakAlias aliases // pkgsRet;

in pkgs
in abiCompatiblePatches pkgs

0 comments on commit 1b2e44e

Please sign in to comment.