[tor-commits] [sandboxed-tor-browser/master] Bug #20899: Dunlib/PulseAudio fixes.

yawning at torproject.org yawning at torproject.org
Wed Dec 7 01:06:21 UTC 2016


commit e68ab9154e73f2fde78ffaf556f39670e4a807f5
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Wed Dec 7 01:05:28 2016 +0000

    Bug #20899: Dunlib/PulseAudio fixes.
    
     * Try even harder to avoid sticking libraries with the wrong architecture
       in the container.
    
     * If libpulsecore can't be found, don't bindmount libpulse.so, since it
       will abort() in the stub.  No audio, but it beats crashing.
---
 .../sandboxed-tor-browser/internal/dynlib/cache.go |  4 +-
 .../sandboxed-tor-browser/internal/dynlib/ldso.go  | 30 +++++++-
 .../internal/sandbox/application.go                | 70 ++++++------------
 .../internal/sandbox/pulse.go                      | 85 +++++++++++++++++++++-
 4 files changed, 135 insertions(+), 54 deletions(-)

diff --git a/src/cmd/sandboxed-tor-browser/internal/dynlib/cache.go b/src/cmd/sandboxed-tor-browser/internal/dynlib/cache.go
index 4bcc60d..11f1ee3 100644
--- a/src/cmd/sandboxed-tor-browser/internal/dynlib/cache.go
+++ b/src/cmd/sandboxed-tor-browser/internal/dynlib/cache.go
@@ -94,7 +94,7 @@ func (c *Cache) ResolveLibraries(binaries []string, extraLibs []string, ldLibrar
 			break
 		}
 		for _, fn := range toCheck {
-			impLibs, err := GetLibraries(fn)
+			impLibs, err := getLibraries(fn)
 			if err != nil {
 				return nil, err
 			}
@@ -373,6 +373,8 @@ func LoadCache() (*Cache, error) {
 		// osVersion, or hwcap.
 		if ourOsVersion < e.osVersion {
 			Debugf("dynlib: ignoring library: %v (osVersion: %x)", e.key, e.osVersion)
+		} else if err = ValidateLibraryClass(e.value); err != nil {
+			Debugf("dynlib: ignoring library %v (%v)", e.key, err)
 		} else if flagCheckFn(e.flags) && capCheckFn(e.hwcap) {
 			vec := c.store[e.key]
 			vec = append(vec, e)
diff --git a/src/cmd/sandboxed-tor-browser/internal/dynlib/ldso.go b/src/cmd/sandboxed-tor-browser/internal/dynlib/ldso.go
index 812a5a6..92e16c1 100644
--- a/src/cmd/sandboxed-tor-browser/internal/dynlib/ldso.go
+++ b/src/cmd/sandboxed-tor-browser/internal/dynlib/ldso.go
@@ -19,6 +19,7 @@ package dynlib
 import (
 	"debug/elf"
 	"errors"
+	"fmt"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -26,9 +27,7 @@ import (
 
 var errUnsupported = errors.New("dynlib: unsupported os/architecture")
 
-// GetLibraries returns the dynamic libraries imported by the given file at
-// dynamic link time.
-func GetLibraries(fn string) ([]string, error) {
+func getLibraries(fn string) ([]string, error) {
 	f, err := elf.Open(fn)
 	if err != nil {
 		return nil, err
@@ -38,6 +37,31 @@ func GetLibraries(fn string) ([]string, error) {
 	return f.ImportedLibraries()
 }
 
+// ValidateLibraryClass ensures that the library matches the current
+// architecture.
+func ValidateLibraryClass(fn string) error {
+	f, err := elf.Open(fn)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	var expectedClass elf.Class
+	switch runtime.GOARCH {
+	case "amd64":
+		expectedClass = elf.ELFCLASS64
+	case "386":
+		expectedClass = elf.ELFCLASS32
+	default:
+		return errUnsupported
+	}
+
+	if f.Class != expectedClass {
+		return fmt.Errorf("unsupported class: %v", fn, f.Class)
+	}
+	return nil
+}
+
 // FindLdSo returns the path to the `ld.so` dynamic linker for the current
 // architecture, which is usually a symlink
 func FindLdSo(cache *Cache) (string, string, error) {
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
index 939d805..4cd4ca0 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
@@ -28,7 +28,6 @@ import (
 	"path/filepath"
 	"runtime"
 	"sort"
-	"strings"
 	"syscall"
 
 	"cmd/sandboxed-tor-browser/internal/dynlib"
@@ -220,54 +219,13 @@ func RunTorBrowser(cfg *config.Config, manif *config.Manifest, tor *tor.Tor) (cm
 		ldLibraryPath = ldLibraryPath + glLibPaths
 
 		if cfg.Sandbox.EnablePulseAudio && pulseAudioWorks {
-			const libPulse = "libpulse.so.0"
-
-			paLibsPath := findDistributionDependentLibs(nil, "", "pulseaudio")
-			if paLibsPath != "" && cache.GetLibraryPath(libPulse) != "" {
-				const restrictedPulseDir = "/usr/lib/pulseaudio"
-
-				// The library search path ("/usr/lib/pulseaudio"), is
-				// hardcoded into libpulse.so.0, because you suck, and we hate
-				// you.
-				extraLibs = append(extraLibs, libPulse)
-				ldLibraryPath = ldLibraryPath + ":" + paLibsPath
-				h.dir(restrictedPulseDir)
-				extraLdLibraryPath = extraLdLibraryPath + ":" + restrictedPulseDir
-
-				boundPulseCore := false
-				matches, err := filepath.Glob(paLibsPath + "/*.so")
-				if err != nil {
-					return nil, err
-				}
-				for _, v := range matches {
-					_, f := filepath.Split(v)
-					if strings.HasPrefix(f, "libpulsecore") {
-						boundPulseCore = true
-					}
-					h.roBind(v, filepath.Join(restrictedPulseDir, f), false)
-					extraLibs = append(extraLibs, f)
-				}
-
-				if !boundPulseCore {
-					// Debian sticks libpulsecore-blah.so in /usr/lib, unlike
-					// everyone else who sticks it in /usr/lib/pulseaudo,
-					// because fuck you.
-					matches, err = filepath.Glob("/usr/lib/libpulsecore-*.so")
-					if err != nil {
-						return nil, err
-					}
-					if len(matches) == 0 {
-						log.Printf("sandbox: Failed to find `libpulsecore-<version>.so`, audio will crash the browser.")
-					} else {
-						for _, v := range matches {
-							_, f := filepath.Split(v)
-							h.roBind(v, filepath.Join(restrictedPulseDir, f), false)
-							extraLibs = append(extraLibs, f)
-						}
-					}
-				}
+			paLibs, paPath, paExtraPath, err := h.appendRestrictedPulseAudio(cache)
+			if err != nil {
+				log.Printf("sandbox: Failed to find PulseAudio libraries: %v", err)
 			} else {
-				log.Printf("sandbox: Failed to find pulse audio libraries.")
+				extraLibs = append(extraLibs, paLibs...)
+				ldLibraryPath = ldLibraryPath + paPath
+				extraLdLibraryPath = extraLdLibraryPath + paExtraPath
 			}
 		}
 		if codec := findBestCodec(cache); codec != "" {
@@ -588,7 +546,21 @@ func findDistributionDependentLibs(extraSearch []string, subDir, fn string) stri
 
 	for _, base := range searchPaths {
 		candidate := filepath.Join(base, subDir, fn)
-		if FileExists(candidate) {
+		if FileExists(candidate) && dynlib.ValidateLibraryClass(candidate) == nil {
+			return candidate
+		}
+	}
+	return ""
+}
+
+func findDistributionDependentDir(extraSearch []string, subDir, fn string) string {
+	var searchPaths []string
+	searchPaths = append(searchPaths, extraSearch...)
+	searchPaths = append(searchPaths, distributionDependentLibSearchPath...)
+
+	for _, base := range searchPaths {
+		candidate := filepath.Join(base, subDir, fn)
+		if DirExists(candidate) {
 			return candidate
 		}
 	}
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/pulse.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/pulse.go
index 0b2fd6d..c58843b 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/pulse.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/pulse.go
@@ -24,6 +24,9 @@ import (
 	"strings"
 
 	xdg "github.com/cep21/xdgbasedir"
+
+	"cmd/sandboxed-tor-browser/internal/dynlib"
+	. "cmd/sandboxed-tor-browser/internal/utils"
 )
 
 func (h *hugbox) enablePulseAudio() error {
@@ -48,7 +51,7 @@ func (h *hugbox) enablePulseAudio() error {
 	}
 
 	if fi, err := os.Stat(sockPath); err != nil {
-		// No pulse Audio socket.
+		// No PulseAudio socket.
 		return fmt.Errorf("sandbox: no PulseAudio socket")
 	} else if fi.Mode()&os.ModeSocket == 0 {
 		// Not an AF_LOCAL socket.
@@ -93,3 +96,83 @@ func (h *hugbox) enablePulseAudio() error {
 
 	return nil
 }
+
+func (h *hugbox) appendRestrictedPulseAudio(cache *dynlib.Cache) ([]string, string, string, error) {
+	const libPulse = "libpulse.so.0"
+
+	type roBindEnt struct {
+		src, dst string
+	}
+	toRoBind := []roBindEnt{}
+
+	extraLibs := []string{}
+	ldLibraryPath := ""
+	extraLdLibraryPath := ""
+
+	paLibsPath := findDistributionDependentDir(nil, "", "pulseaudio")
+	if paLibsPath != "" && cache.GetLibraryPath(libPulse) != "" {
+		const restrictedPulseDir = "/usr/lib/pulseaudio"
+
+		// The library search path ("/usr/lib/pulseaudio"), is
+		// hardcoded into libpulse.so.0, because you suck, and we hate
+		// you.
+
+		extraLibs = append(extraLibs, libPulse)
+		ldLibraryPath = ldLibraryPath + ":" + paLibsPath
+		extraLdLibraryPath = extraLdLibraryPath + ":" + restrictedPulseDir
+
+		// The special handling for libpulsecore is because, we need to dlopen
+		// it in our stub.
+
+		boundPulseCore := false
+		matches, err := filepath.Glob(paLibsPath + "/*.so")
+		if err != nil {
+			return nil, "", "", err
+		}
+		for _, v := range matches {
+			if dynlib.ValidateLibraryClass(v) != nil {
+				Debugf("sandbox: Unsuitable PulseAudio so: %v", v)
+				continue
+			}
+			_, f := filepath.Split(v)
+			if strings.HasPrefix(f, "libpulsecore") {
+				boundPulseCore = true
+			}
+			toRoBind = append(toRoBind, roBindEnt{v, filepath.Join(restrictedPulseDir, f)})
+			extraLibs = append(extraLibs, f)
+		}
+
+		// Debian sticks libpulsecore-blah.so in /usr/lib, unlike
+		// everyone else who sticks it in /usr/lib/pulseaudo,
+		// because fuck you.
+		if !boundPulseCore {
+			matches, err = filepath.Glob("/usr/lib/libpulsecore-*.so")
+			if err != nil {
+				return nil, "", "", err
+			}
+			for _, v := range matches {
+				if dynlib.ValidateLibraryClass(v) != nil {
+					Debugf("sandbox: Unsuitable pulsecore: %v", v)
+					continue
+				}
+				_, f := filepath.Split(v)
+				toRoBind = append(toRoBind, roBindEnt{v, filepath.Join(restrictedPulseDir, f)})
+				extraLibs = append(extraLibs, f)
+				boundPulseCore = true
+				break
+			}
+		}
+
+		// Now that we're done trying to find all the PulseAudio bits,
+		// actually bindmount everything into the sandbox.
+		if boundPulseCore {
+			h.dir(restrictedPulseDir)
+			for _, ent := range toRoBind {
+				h.roBind(ent.src, ent.dst, false)
+			}
+			return extraLibs, ldLibraryPath, extraLdLibraryPath, nil
+		}
+	}
+
+	return nil, "", "", fmt.Errorf("failed to find PulseAudio libraries")
+}



More information about the tor-commits mailing list