Skip to content

Commit

Permalink
Add more spans to CheckExecutable
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany committed Jan 24, 2025
1 parent 0da640e commit 16a35a8
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 28 deletions.
37 changes: 24 additions & 13 deletions ee/tuf/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import (
// CheckExecutable tests whether something is an executable. It
// examines permissions, mode, and tries to exec it directly.
func CheckExecutable(ctx context.Context, potentialBinary string, args ...string) error {
ctx, span := traces.StartSpan(ctx)
ctx, span := traces.StartSpan(ctx, "binary_path", potentialBinary)
defer span.End()

if err := checkExecutablePermissions(potentialBinary); err != nil {
if err := checkExecutablePermissions(ctx, potentialBinary); err != nil {
return err
}

Expand All @@ -36,15 +36,7 @@ func CheckExecutable(ctx context.Context, potentialBinary string, args ...string
// in that circumstance.
// See: https://github.com/golang/go/issues/22315
for i := 0; i < 3; i += 1 {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, potentialBinary, args...) //nolint:forbidigo // We trust the autoupdate library to find the correct location so we don't need allowedcmd

// Set env, this should prevent launcher for fork-bombing
cmd.Env = append(cmd.Env, "LAUNCHER_SKIP_UPDATES=TRUE")

out, execErr := cmd.CombinedOutput()
out, execErr := runExecutableCheck(ctx, potentialBinary, args...)
if execErr != nil && errors.Is(execErr, syscall.ETXTBSY) {
continue
}
Expand All @@ -53,17 +45,36 @@ func CheckExecutable(ctx context.Context, potentialBinary string, args ...string
return fmt.Errorf("timeout when checking executable: %w", ctx.Err())
}

return supressRoutineErrors(execErr, out)
return supressRoutineErrors(ctx, execErr, out)
}

return fmt.Errorf("could not exec %s despite retries due to text file busy", potentialBinary)
}

// runExecutableCheck runs a single exec against the given binary and returns the result.
func runExecutableCheck(ctx context.Context, potentialBinary string, args ...string) ([]byte, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, potentialBinary, args...) //nolint:forbidigo // We trust the autoupdate library to find the correct location so we don't need allowedcmd

// Set env, this should prevent launcher for fork-bombing
cmd.Env = append(cmd.Env, "LAUNCHER_SKIP_UPDATES=TRUE")

return cmd.CombinedOutput()
}

// supressRoutineErrors attempts to tell whether the error was a
// program that has executed, and then exited, vs one that's execution
// was entirely unsuccessful. This differentiation allows us to
// detect, and recover, from corrupt updates vs something in-app.
func supressRoutineErrors(err error, combinedOutput []byte) error {
func supressRoutineErrors(ctx context.Context, err error, combinedOutput []byte) error {
_, span := traces.StartSpan(ctx)
defer span.End()

if err == nil {
return nil
}
Expand Down
8 changes: 7 additions & 1 deletion ee/tuf/util_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
package tuf

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"

"github.com/kolide/launcher/pkg/traces"
)

// executableLocation returns the path to the executable in `updateDirectory`.
Expand All @@ -33,7 +36,10 @@ func executableLocation(updateDirectory string, binary autoupdatableBinary) stri
// checkExecutablePermissions checks whether a specific file looks
// like it's executable. This is used in evaluating whether something
// is an updated version.
func checkExecutablePermissions(potentialBinary string) error {
func checkExecutablePermissions(ctx context.Context, potentialBinary string) error {
_, span := traces.StartSpan(ctx)
defer span.End()

if potentialBinary == "" {
return errors.New("empty string isn't executable")
}
Expand Down
8 changes: 7 additions & 1 deletion ee/tuf/util_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
package tuf

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"

"github.com/kolide/launcher/pkg/traces"
)

// executableLocation returns the path to the executable in `updateDirectory`.
Expand All @@ -18,7 +21,10 @@ func executableLocation(updateDirectory string, binary autoupdatableBinary) stri
// checkExecutablePermissions checks whether a specific file looks
// like it's executable. This is used in evaluating whether something
// is an updated version.
func checkExecutablePermissions(potentialBinary string) error {
func checkExecutablePermissions(ctx context.Context, potentialBinary string) error {
_, span := traces.StartSpan(ctx)
defer span.End()

if potentialBinary == "" {
return errors.New("empty string isn't executable")
}
Expand Down
24 changes: 12 additions & 12 deletions ee/tuf/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ func windowsAddExe(in string) string {
func Test_checkExecutablePermissions(t *testing.T) {
t.Parallel()

require.Error(t, checkExecutablePermissions(""), "passing empty string")
require.Error(t, checkExecutablePermissions("/random/path/should/not/exist"), "passing non-existent file path")
require.Error(t, checkExecutablePermissions(context.TODO(), ""), "passing empty string")
require.Error(t, checkExecutablePermissions(context.TODO(), "/random/path/should/not/exist"), "passing non-existent file path")

// Setup the tests
tmpDir := t.TempDir()

require.Error(t, checkExecutablePermissions(tmpDir), "directory should not be executable")
require.Error(t, checkExecutablePermissions(context.TODO(), tmpDir), "directory should not be executable")

dotExe := ""
if runtime.GOOS == "windows" {
Expand All @@ -222,17 +222,17 @@ func Test_checkExecutablePermissions(t *testing.T) {

// windows doesn't have an executable bit
if runtime.GOOS == "windows" {
require.NoError(t, checkExecutablePermissions(fileName), "plain file")
require.NoError(t, checkExecutablePermissions(hardLink), "hard link")
require.NoError(t, checkExecutablePermissions(symLink), "symlink")
require.NoError(t, checkExecutablePermissions(context.TODO(), fileName), "plain file")
require.NoError(t, checkExecutablePermissions(context.TODO(), hardLink), "hard link")
require.NoError(t, checkExecutablePermissions(context.TODO(), symLink), "symlink")
} else {
require.Error(t, checkExecutablePermissions(fileName), "plain file")
require.Error(t, checkExecutablePermissions(hardLink), "hard link")
require.Error(t, checkExecutablePermissions(symLink), "symlink")
require.Error(t, checkExecutablePermissions(context.TODO(), fileName), "plain file")
require.Error(t, checkExecutablePermissions(context.TODO(), hardLink), "hard link")
require.Error(t, checkExecutablePermissions(context.TODO(), symLink), "symlink")

require.NoError(t, os.Chmod(fileName, 0755))
require.NoError(t, checkExecutablePermissions(fileName), "plain file")
require.NoError(t, checkExecutablePermissions(hardLink), "hard link")
require.NoError(t, checkExecutablePermissions(symLink), "symlink")
require.NoError(t, checkExecutablePermissions(context.TODO(), fileName), "plain file")
require.NoError(t, checkExecutablePermissions(context.TODO(), hardLink), "hard link")
require.NoError(t, checkExecutablePermissions(context.TODO(), symLink), "symlink")
}
}
8 changes: 7 additions & 1 deletion ee/tuf/util_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
package tuf

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/kolide/launcher/pkg/traces"
)

// executableLocation returns the path to the executable in `updateDirectory`.
Expand All @@ -22,7 +25,10 @@ func executableLocation(updateDirectory string, binary autoupdatableBinary) stri
//
// Windows does not have executable bits, so we omit those. And
// instead check the file extension.
func checkExecutablePermissions(potentialBinary string) error {
func checkExecutablePermissions(ctx context.Context, potentialBinary string) error {
_, span := traces.StartSpan(ctx)
defer span.End()

if potentialBinary == "" {
return errors.New("empty string isn't executable")
}
Expand Down

0 comments on commit 16a35a8

Please sign in to comment.