Skip to content

Commit

Permalink
feat(s/i/cpuproxy): custom cpuproxy for android
Browse files Browse the repository at this point in the history
This diff pulls code from https://go-review.googlesource.com/c/sys/+/197540/
to add better support for detecting arm64 CPU features.

I extracted this diff from 62177de with
the intent of coming up with a more manageable patch set.

The idea here is to use this better cpuproxy functionality to feed
crypto libraries with better understanding of cpu capabilities.

The original issue describing this problem in its full extent was
ooni/probe#1444.

We're now working to forward-port this patch to go1.17.

AFAICT, as of go1.17, the src/runtime/os_linux.go still falls back to
reduce functionality in case /proc/self/auxv isn't readable:

https://github.com/golang/go/blob/go1.17.3/src/runtime/os_linux.go#L216

The current issue describing this problem as of go1.17 is
ooni/probe#1863.

Is there a better way?
  • Loading branch information
bassosimone committed Nov 10, 2021
1 parent f58c78a commit 7807667
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
79 changes: 79 additions & 0 deletions src/internal/cpuproxy/cpuproxy_android_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// +build android
// +build arm64

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// This file is based on the diff available at
// https://go-review.googlesource.com/c/sys/+/197540/

package cpuproxy

/*
#include <sys/auxv.h>
// getauxval is not available on Android until API level 20. Link it as a weak
// symbol and check whether it is not NULL before using it.
unsigned long getauxval(unsigned long type) __attribute__((weak));
*/
import "C"

import "sync"

// These constants are Linux specific.
const (
_AT_HWCAP = 16 // hardware capability bit vector
)

// get returns the value of the getauxval auxiliary vector, or
// zero where the functionality is unavailable.
func get(t uint) uint {
if C.getauxval == C.NULL {
return 0
}
return uint(C.getauxval(C.ulong(t)))
}

// dogethwcap returns the value of _AT_HWCAP by calling
// the getauxval(3) function in libc (if available).
func dogethwcap() uint {
return get(_AT_HWCAP)
}

// These variables allow to cache getauxval(3) results.
var (
once sync.Once
hwcap uint
)

// gethwcap is like dogethwcap except that this function
// ensures we call getauxval(3) just once. After the first
// invocation we memoize the result.
func gethwcap() uint {
once.Do(func() {
hwcap = dogethwcap()
})
return hwcap
}

// HWCAP bits. These are exposed by Linux.
const (
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_CPUID = 1 << 11
)

// HasAES returns whether the CPU supports AES.
func HasAES() bool {
return (gethwcap() & hwcap_AES) != 0
}

// HasGFMUL returns whether the CPU supports GFMUL.
func HasGFMUL() bool {
return (gethwcap() & hwcap_PMULL) != 0
}
15 changes: 15 additions & 0 deletions src/internal/cpuproxy/cpuproxy_otherwise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// +build !android !arm64

package cpuproxy

import "internal/cpu"

// HasAES returns whether the CPU supports AES.
func HasAES() bool {
return cpu.X86.HasAES || cpu.ARM64.HasAES
}

// HasGFMUL returns whether the CPU supports GFMUL.
func HasGFMUL() bool {
return cpu.X86.HasPCLMULQDQ || cpu.ARM64.HasPMULL
}
22 changes: 22 additions & 0 deletions src/internal/cpuproxy/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Package cpuproxy is a getauxval aware proxy for internal/cpu.
//
// The problem that we want to solve is that on Android there are
// cases where reading /proc/self/auxv is not possible.
//
// This causes crypto/tls to not choose AES where it would otherwise
// be possible, in turn causing censorship. See also the
// https://github.com/ooni/probe/issues/1444 issue for more details.
//
// Ideally we would like to call getauxval(3) when initializing
// the runtime package. However, runtime cannot use CGO. Doing that
// leads to an import loop, so we cannot build.
//
// We could also try to parse /proc/cpuinfo (I didn't explore this route).
//
// The solution chosen here is to export predicates on the CPU
// functionality. We limit ourselves to what we need in order to
// choose AES in crypto/tls when the CPU supports it.
//
// The predicates use internal/cpu values for every GOOS/GOARCH except
// android/arm64, where we call getauxval(3).
package cpuproxy

0 comments on commit 7807667

Please sign in to comment.