Skip to content

Commit efdb9b1

Browse files
dantecatalfamosharon-fdm
authored andcommitted
Interpret windows exit codes as a signed integer (#18282)
#17695 The windows exit code is a 32-bit unsigned integer, but the command interpreter treats it like a signed integer. When a process is killed, it returns 0xFFFFFFFF (interpreted as -1). We convert the integer to an signed 32-bit integer to flip it to a -1 to match our expectations, and fit in our db column. https://en.wikipedia.org/wiki/Exit_status#Windows FIxed on both the client and server side.
1 parent 1f4bfc0 commit efdb9b1

File tree

5 files changed

+38
-2
lines changed

5 files changed

+38
-2
lines changed
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* Cast windows exit codes to signed integers to match windows interpreter
2+
* Fix bug where some scripts got stuck in "upcoming" activity permanently
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Cast windows exit codes to signed integers to match windows interpreter

orbit/pkg/scripts/exec_windows.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ func execCmd(ctx context.Context, scriptPath string) (output []byte, exitCode in
1717
cmd.Dir = filepath.Dir(scriptPath)
1818
output, err = cmd.CombinedOutput()
1919
if cmd.ProcessState != nil {
20-
exitCode = cmd.ProcessState.ExitCode()
20+
// The windows exit code is a 32-bit unsigned integer, but the
21+
// interpreter treats it like a signed integer. When a process
22+
// is killed, it returns 0xFFFFFFFF (interpreted as -1). We
23+
// convert the integer to an signed 32-bit integer to flip it
24+
// to a -1 to match our expectations, and fit in our db column.
25+
//
26+
// https://en.wikipedia.org/wiki/Exit_status#Windows
27+
exitCode = int(int32(cmd.ProcessState.ExitCode()))
2128
}
2229
return output, exitCode, err
2330
}

server/datastore/mysql/scripts.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,12 @@ func (ds *Datastore) SetHostScriptExecutionResult(ctx context.Context, result *f
128128
res, err := tx.ExecContext(ctx, updStmt,
129129
output,
130130
result.Runtime,
131-
result.ExitCode,
131+
// Windows error codes are signed 32-bit integers, but are
132+
// returned as unsigned integers by the windows API. The
133+
// software that receives them is responsible for casting
134+
// it to a 32-bit signed integer.
135+
// See /orbit/pkg/scripts/exec_windows.go
136+
int32(result.ExitCode),
132137
result.HostID,
133138
result.ExecutionID,
134139
)

server/datastore/mysql/scripts_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
_ "embed"
66
"fmt"
7+
"math"
78
"strings"
89
"testing"
910
"time"
@@ -220,6 +221,26 @@ func testHostScriptResult(t *testing.T, ds *Datastore) {
220221
pending, err = ds.ListPendingHostScriptExecutions(ctx, 1)
221222
require.NoError(t, err)
222223
require.Empty(t, pending, 0)
224+
225+
// check that scripts with large unsigned error codes get
226+
// converted to signed error codes
227+
createdUnsignedScript, err := ds.NewHostScriptExecutionRequest(ctx, &fleet.HostScriptRequestPayload{
228+
HostID: 1,
229+
ScriptContents: "echo",
230+
UserID: &u.ID,
231+
SyncRequest: true,
232+
})
233+
require.NoError(t, err)
234+
235+
unsignedScriptResult, err := ds.SetHostScriptExecutionResult(ctx, &fleet.HostScriptResultPayload{
236+
HostID: 1,
237+
ExecutionID: createdUnsignedScript.ExecutionID,
238+
Output: "foo",
239+
Runtime: 1,
240+
ExitCode: math.MaxUint32,
241+
})
242+
require.NoError(t, err)
243+
require.EqualValues(t, -1, *unsignedScriptResult.ExitCode)
223244
}
224245

225246
func testScripts(t *testing.T, ds *Datastore) {

0 commit comments

Comments
 (0)