File size: 2,355 Bytes
d61821a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
diff --git a/common/build.go b/common/build.go
index 1d79b330..fd7ae93f 100644
--- a/common/build.go
+++ b/common/build.go
@@ -529,6 +529,15 @@ func wrapStepStageErr(err error) error {
 
 	berr := &BuildError{Inner: err}
 
+	// Classify step-runner internal failures (gRPC handler panics and
+	// ErrorInternal job statuses) as ScriptFailure rather than
+	// RunnerSystemFailure: a malicious job could deliberately trigger either
+	// path to forge a RunnerSystemFailure and evade job-failure accounting.
+	var cierr *steps.ClientInternalError
+	if errors.As(err, &cierr) {
+		berr.FailureReason = ScriptFailure
+	}
+
 	var cserr *steps.ClientStatusError
 	if errors.As(err, &cserr) {
 		switch cserr.Status.ErrorKind {
diff --git a/steps/execute.go b/steps/execute.go
index 63837242..76e564e4 100644
--- a/steps/execute.go
+++ b/steps/execute.go
@@ -9,7 +9,9 @@ import (
 	"time"
 
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials/insecure"
+	grpcstatus "google.golang.org/grpc/status"
 
 	"gitlab.com/gitlab-org/gitlab-runner/common/spec"
 	"gitlab.com/gitlab-org/step-runner/pkg/api/client"
@@ -50,6 +52,17 @@ func (cserr *ClientStatusError) Unwrap() error {
 	return cserr.Err
 }
 
+// ClientInternalError signals a step-runner client failure that is not tied
+// to a job Status — specifically, a gRPC handler panic surfacing as
+// codes.Internal via step-runner's panic-recovery interceptor. Distinct from
+// ClientStatusError, which reports a Status returned by step-runner.
+type ClientInternalError struct {
+	Err error
+}
+
+func (e *ClientInternalError) Error() string { return e.Err.Error() }
+func (e *ClientInternalError) Unwrap() error { return e.Err }
+
 func Execute(ctx context.Context, connector Connector, jobInfo JobInfo, steps []schema.Step, trace io.Writer) error {
 	dialFn, err := connector.Connect(ctx)
 	if err != nil {
@@ -73,7 +86,11 @@ func Execute(ctx context.Context, connector Connector, jobInfo JobInfo, steps []
 
 	status, err := c.RunAndFollow(ctx, request, &out)
 	if err != nil {
-		return fmt.Errorf("executing steps request: %w", err)
+		wrapped := fmt.Errorf("executing steps request: %w", err)
+		if grpcstatus.Code(err) == codes.Internal {
+			return &ClientInternalError{Err: wrapped}
+		}
+		return wrapped
 	}
 
 	if status.State == client.StateSuccess {