Skip to content

Commit feebc3c

Browse files
🍒 8549 - Prevent before callsites targeting constructors in super calls (#8582)
* Prevent before callsites targeting calls to super in constructors (cherry picked from commit e20ae64) * Fix failing tests (cherry picked from commit 70c362c)
1 parent 6c61c65 commit feebc3c

File tree

18 files changed

+226
-33
lines changed

18 files changed

+226
-33
lines changed

buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/impl/AdviceGeneratorImpl.java

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.github.javaparser.ast.Modifier.Keyword.PUBLIC;
44
import static datadog.trace.plugin.csi.impl.CallSiteFactory.typeResolver;
5+
import static datadog.trace.plugin.csi.util.CallSiteConstants.ADVICE_TYPE_CLASS;
56
import static datadog.trace.plugin.csi.util.CallSiteConstants.AUTO_SERVICE_FQDN;
67
import static datadog.trace.plugin.csi.util.CallSiteConstants.CALL_SITES_CLASS;
78
import static datadog.trace.plugin.csi.util.CallSiteConstants.CALL_SITES_FQCN;
@@ -185,20 +186,24 @@ private void addAdviceLambda(
185186
final MethodType pointCut = spec.getPointcut();
186187
final BlockStmt adviceBody = new BlockStmt();
187188
final Expression advice;
189+
final String type;
188190
if (spec.isInvokeDynamic()) {
189191
advice = invokeDynamicAdviceSignature(adviceBody);
190192
} else {
191193
advice = invokeAdviceSignature(adviceBody);
192194
}
193195
if (spec instanceof BeforeSpecification) {
196+
type = "BEFORE";
194197
writeStackOperations(spec, adviceBody);
195198
writeAdviceMethodCall(spec, adviceBody);
196199
writeOriginalMethodCall(spec, adviceBody);
197200
} else if (spec instanceof AfterSpecification) {
201+
type = "AFTER";
198202
writeStackOperations(spec, adviceBody);
199203
writeOriginalMethodCall(spec, adviceBody);
200204
writeAdviceMethodCall(spec, adviceBody);
201205
} else {
206+
type = "AROUND";
202207
writeAdviceMethodCall(spec, adviceBody);
203208
}
204209
body.addStatement(
@@ -207,6 +212,10 @@ private void addAdviceLambda(
207212
.setName("addAdvice")
208213
.setArguments(
209214
new NodeList<>(
215+
new FieldAccessExpr()
216+
.setScope(
217+
new TypeExpr(new ClassOrInterfaceType().setName(ADVICE_TYPE_CLASS)))
218+
.setName(type),
210219
new StringLiteralExpr(pointCut.getOwner().getInternalName()),
211220
new StringLiteralExpr(pointCut.getMethodName()),
212221
new StringLiteralExpr(pointCut.getMethodType().getDescriptor()),

buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/impl/ext/IastExtension.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,19 @@ private static LambdaExpr findAdviceLambda(
364364
final MethodType pointcut = spec.getPointcut();
365365
for (final MethodCallExpr add : addAdvices) {
366366
final NodeList<Expression> arguments = add.getArguments();
367-
final String owner = arguments.get(0).asStringLiteralExpr().asString();
367+
final String owner = arguments.get(1).asStringLiteralExpr().asString();
368368
if (!owner.equals(pointcut.getOwner().getInternalName())) {
369369
continue;
370370
}
371-
final String method = arguments.get(1).asStringLiteralExpr().asString();
371+
final String method = arguments.get(2).asStringLiteralExpr().asString();
372372
if (!method.equals(pointcut.getMethodName())) {
373373
continue;
374374
}
375-
final String description = arguments.get(2).asStringLiteralExpr().asString();
375+
final String description = arguments.get(3).asStringLiteralExpr().asString();
376376
if (!description.equals(pointcut.getMethodType().getDescriptor())) {
377377
continue;
378378
}
379-
return arguments.get(3).asLambdaExpr();
379+
return arguments.get(4).asLambdaExpr();
380380
}
381381
throw new IllegalArgumentException("Cannot find lambda expression for pointcut " + pointcut);
382382
}

buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/util/CallSiteConstants.java

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ private CallSiteConstants() {}
3030

3131
public static final String HAS_ENABLED_PROPERTY_CLASS = CALL_SITES_CLASS + ".HasEnabledProperty";
3232

33+
public static final String ADVICE_TYPE_CLASS = "AdviceType";
34+
3335
public static final String STACK_DUP_MODE_CLASS = "StackDupMode";
3436

3537
public static final String METHOD_HANDLER_CLASS = "MethodHandler";

buildSrc/call-site-instrumentation-plugin/src/test/groovy/datadog/trace/plugin/csi/impl/AdviceGeneratorTest.groovy

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ final class AdviceGeneratorTest extends BaseCsiPluginTest {
4444
interfaces(CallSites)
4545
helpers(BeforeAdvice)
4646
advices(0) {
47+
type("BEFORE")
4748
pointcut('java/security/MessageDigest', 'getInstance', '(Ljava/lang/String;)Ljava/security/MessageDigest;')
4849
statements(
4950
'handler.dupParameters(descriptor, StackDupMode.COPY);',
@@ -76,6 +77,7 @@ final class AdviceGeneratorTest extends BaseCsiPluginTest {
7677
interfaces(CallSites)
7778
helpers(AroundAdvice)
7879
advices(0) {
80+
type("AROUND")
7981
pointcut('java/lang/String', 'replaceAll', '(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;')
8082
statements(
8183
'handler.advice("datadog/trace/plugin/csi/impl/AdviceGeneratorTest$AroundAdvice", "around", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");'
@@ -106,6 +108,7 @@ final class AdviceGeneratorTest extends BaseCsiPluginTest {
106108
interfaces(CallSites)
107109
helpers(AfterAdvice)
108110
advices(0) {
111+
type("AFTER")
109112
pointcut('java/lang/String', 'concat', '(Ljava/lang/String;)Ljava/lang/String;')
110113
statements(
111114
'handler.dupInvoke(owner, descriptor, StackDupMode.COPY);',

buildSrc/call-site-instrumentation-plugin/src/test/groovy/datadog/trace/plugin/csi/impl/assertion/AdviceAssert.groovy

+5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package datadog.trace.plugin.csi.impl.assertion
22

33
class AdviceAssert {
4+
protected String type
45
protected String owner
56
protected String method
67
protected String descriptor
78
protected Collection<String> statements
89

10+
void type(String type) {
11+
assert type == this.type
12+
}
13+
914
void pointcut(String owner, String method, String descriptor) {
1015
assert owner == this.owner
1116
assert method == this.method

buildSrc/call-site-instrumentation-plugin/src/test/groovy/datadog/trace/plugin/csi/impl/assertion/AssertBuilder.groovy

+4-2
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ class AssertBuilder<C extends CallSiteAssert> {
8383
return getMethodCalls(acceptMethod).findAll {
8484
it.nameAsString == 'addAdvice'
8585
}.collect {
86-
def (owner, method, descriptor) = it.arguments.subList(0, 3)*.asStringLiteralExpr()*.asString()
87-
final handlerLambda = it.arguments[3].asLambdaExpr()
86+
final adviceType = it.arguments.get(0).asFieldAccessExpr().getName()
87+
def (owner, method, descriptor) = it.arguments.subList(1, 4)*.asStringLiteralExpr()*.asString()
88+
final handlerLambda = it.arguments[4].asLambdaExpr()
8889
final advice = handlerLambda.body.asBlockStmt().statements*.toString()
8990
return new AdviceAssert([
91+
type : adviceType,
9092
owner : owner,
9193
method : method,
9294
descriptor: descriptor,

buildSrc/call-site-instrumentation-plugin/src/test/groovy/datadog/trace/plugin/csi/impl/ext/IastExtensionTest.groovy

+2-2
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ class IastExtensionTest extends BaseCsiPluginTest {
217217
return getMethodCalls(acceptMethod).findAll {
218218
it.nameAsString == 'addAdvice'
219219
}.collect {
220-
def (owner, method, descriptor) = it.arguments.subList(0, 3)*.asStringLiteralExpr()*.asString()
221-
final handlerLambda = it.arguments[3].asLambdaExpr()
220+
def (owner, method, descriptor) = it.arguments.subList(1, 4)*.asStringLiteralExpr()*.asString()
221+
final handlerLambda = it.arguments[4].asLambdaExpr()
222222
final statements = handlerLambda.body.asBlockStmt().statements
223223
final instrumentedStmt = statements.get(0).asIfStmt()
224224
final executedStmt = statements.get(1).asIfStmt()

dd-java-agent/agent-tooling/src/jmh/java/datadog/trace/agent/tooling/bytebuddy/csi/CallSiteBenchmarkInstrumentation.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.trace.agent.tooling.bytebuddy.csi;
22

3+
import static datadog.trace.agent.tooling.csi.CallSiteAdvice.AdviceType.AROUND;
4+
35
import datadog.trace.agent.tooling.csi.CallSites;
46
import datadog.trace.agent.tooling.csi.InvokeAdvice;
57
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher;
@@ -44,6 +46,7 @@ public Iterable<CallSites> get() {
4446
return Collections.singletonList(
4547
(container -> {
4648
container.addAdvice(
49+
AROUND,
4750
"javax/servlet/ServletRequest",
4851
"getParameter",
4952
"(Ljava/lang/String;)Ljava/lang/String;",

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/csi/Advices.java

+75-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import datadog.trace.agent.tooling.bytebuddy.ClassFileLocators;
77
import datadog.trace.agent.tooling.csi.CallSiteAdvice;
88
import datadog.trace.agent.tooling.csi.CallSites;
9+
import datadog.trace.agent.tooling.csi.InvokeAdvice;
10+
import datadog.trace.agent.tooling.csi.InvokeDynamicAdvice;
911
import java.lang.reflect.Field;
1012
import java.util.Arrays;
1113
import java.util.Collections;
@@ -141,6 +143,11 @@ public CallSiteAdvice findAdvice(
141143
return methodAdvices.get(descriptor);
142144
}
143145

146+
/** Gets the type of advice we are dealing with */
147+
public byte typeOf(final CallSiteAdvice advice) {
148+
return ((TypedAdvice) advice).getType();
149+
}
150+
144151
public String[] getHelpers() {
145152
return helpers;
146153
}
@@ -176,15 +183,17 @@ public void addHelpers(final String... helperClassNames) {
176183

177184
@Override
178185
public void addAdvice(
179-
final String type,
186+
final byte type,
187+
final String owner,
180188
final String method,
181189
final String descriptor,
182190
final CallSiteAdvice advice) {
183191
final Map<String, Map<String, CallSiteAdvice>> typeAdvices =
184-
advices.computeIfAbsent(type, k -> new HashMap<>());
192+
advices.computeIfAbsent(owner, k -> new HashMap<>());
185193
final Map<String, CallSiteAdvice> methodAdvices =
186194
typeAdvices.computeIfAbsent(method, k -> new HashMap<>());
187-
final CallSiteAdvice oldAdvice = methodAdvices.put(descriptor, advice);
195+
final CallSiteAdvice oldAdvice =
196+
methodAdvices.put(descriptor, TypedAdvice.withType(advice, type));
188197
if (oldAdvice != null) {
189198
throw new UnsupportedOperationException(
190199
String.format(
@@ -360,4 +369,67 @@ public interface Listener {
360369
void onConstantPool(
361370
@Nonnull TypeDescription type, @Nonnull ConstantPool pool, final byte[] classFile);
362371
}
372+
373+
private interface TypedAdvice {
374+
byte getType();
375+
376+
static CallSiteAdvice withType(final CallSiteAdvice advice, final byte type) {
377+
if (advice instanceof InvokeAdvice) {
378+
return new InvokeWithType((InvokeAdvice) advice, type);
379+
} else {
380+
return new InvokeDynamicWithType((InvokeDynamicAdvice) advice, type);
381+
}
382+
}
383+
}
384+
385+
private static class InvokeWithType implements InvokeAdvice, TypedAdvice {
386+
private final InvokeAdvice advice;
387+
private final byte type;
388+
389+
private InvokeWithType(InvokeAdvice advice, byte type) {
390+
this.advice = advice;
391+
this.type = type;
392+
}
393+
394+
@Override
395+
public byte getType() {
396+
return type;
397+
}
398+
399+
@Override
400+
public void apply(
401+
final MethodHandler handler,
402+
final int opcode,
403+
final String owner,
404+
final String name,
405+
final String descriptor,
406+
final boolean isInterface) {
407+
advice.apply(handler, opcode, owner, name, descriptor, isInterface);
408+
}
409+
}
410+
411+
private static class InvokeDynamicWithType implements InvokeDynamicAdvice, TypedAdvice {
412+
private final InvokeDynamicAdvice advice;
413+
private final byte type;
414+
415+
private InvokeDynamicWithType(final InvokeDynamicAdvice advice, final byte type) {
416+
this.advice = advice;
417+
this.type = type;
418+
}
419+
420+
@Override
421+
public byte getType() {
422+
return type;
423+
}
424+
425+
@Override
426+
public void apply(
427+
final MethodHandler handler,
428+
final String name,
429+
final String descriptor,
430+
final Handle bootstrapMethodHandle,
431+
final Object... bootstrapMethodArguments) {
432+
advice.apply(handler, name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
433+
}
434+
}
363435
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/csi/CallSiteTransformer.java

+45-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.agent.tooling.bytebuddy.csi;
22

3+
import static datadog.trace.agent.tooling.csi.CallSiteAdvice.AdviceType.AFTER;
34
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
45
import static net.bytebuddy.jar.asm.ClassWriter.COMPUTE_MAXS;
56

@@ -126,7 +127,7 @@ public MethodVisitor visitMethod(
126127

127128
private static class CallSiteMethodVisitor extends MethodVisitor
128129
implements CallSiteAdvice.MethodHandler {
129-
private final Advices advices;
130+
protected final Advices advices;
130131

131132
private CallSiteMethodVisitor(
132133
@Nonnull final Advices advices, @Nonnull final MethodVisitor delegated) {
@@ -144,12 +145,22 @@ public void visitMethodInsn(
144145

145146
CallSiteAdvice advice = advices.findAdvice(owner, name, descriptor);
146147
if (advice instanceof InvokeAdvice) {
147-
((InvokeAdvice) advice).apply(this, opcode, owner, name, descriptor, isInterface);
148+
invokeAdvice((InvokeAdvice) advice, opcode, owner, name, descriptor, isInterface);
148149
} else {
149150
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
150151
}
151152
}
152153

154+
protected void invokeAdvice(
155+
final InvokeAdvice advice,
156+
final int opcode,
157+
final String owner,
158+
final String name,
159+
final String descriptor,
160+
final boolean isInterface) {
161+
advice.apply(this, opcode, owner, name, descriptor, isInterface);
162+
}
163+
153164
@Override
154165
public void visitInvokeDynamicInsn(
155166
final String name,
@@ -158,14 +169,27 @@ public void visitInvokeDynamicInsn(
158169
final Object... bootstrapMethodArguments) {
159170
CallSiteAdvice advice = advices.findAdvice(bootstrapMethodHandle);
160171
if (advice instanceof InvokeDynamicAdvice) {
161-
((InvokeDynamicAdvice) advice)
162-
.apply(this, name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
172+
invokeDynamicAdvice(
173+
(InvokeDynamicAdvice) advice,
174+
name,
175+
descriptor,
176+
bootstrapMethodHandle,
177+
bootstrapMethodArguments);
163178
} else {
164179
mv.visitInvokeDynamicInsn(
165180
name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
166181
}
167182
}
168183

184+
protected void invokeDynamicAdvice(
185+
final InvokeDynamicAdvice advice,
186+
final String name,
187+
final String descriptor,
188+
final Handle bootstrapMethodHandle,
189+
final Object... bootstrapMethodArguments) {
190+
advice.apply(this, name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
191+
}
192+
169193
@Override
170194
public void instruction(final int opcode) {
171195
mv.visitInsn(opcode);
@@ -347,5 +371,22 @@ public void dupParameters(final String methodDescriptor, final StackDupMode mode
347371
super.dupParameters(
348372
methodDescriptor, isSuperCall ? StackDupMode.PREPEND_ARRAY_SUPER_CTOR : mode);
349373
}
374+
375+
@Override
376+
protected void invokeAdvice(
377+
final InvokeAdvice advice,
378+
final int opcode,
379+
final String owner,
380+
final String name,
381+
final String descriptor,
382+
final boolean isInterface) {
383+
if (isSuperCall && advices.typeOf(advice) != AFTER) {
384+
// TODO APPSEC-57009 calls to super are only instrumented by after call sites
385+
// just ignore the advice and keep on
386+
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
387+
} else {
388+
super.invokeAdvice(advice, opcode, owner, name, descriptor, isInterface);
389+
}
390+
}
350391
}
351392
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/csi/CallSiteAdvice.java

+9
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,13 @@ enum StackDupMode {
7272
/** Copies the parameters in an array and appends it */
7373
APPEND_ARRAY
7474
}
75+
76+
abstract class AdviceType {
77+
78+
private AdviceType() {}
79+
80+
public static final byte BEFORE = -1;
81+
public static final byte AROUND = 0;
82+
public static final byte AFTER = 1;
83+
}
7584
}

0 commit comments

Comments
 (0)