1 // Copyright 2022 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.code_intelligence.jazzer.instrumentor
16 
17 import org.objectweb.asm.MethodVisitor
18 import org.objectweb.asm.Opcodes
19 
20 object DirectByteBufferStrategy : EdgeCoverageStrategy {
21 
instrumentControlFlowEdgenull22     override fun instrumentControlFlowEdge(
23         mv: MethodVisitor,
24         edgeId: Int,
25         variable: Int,
26         coverageMapInternalClassName: String,
27     ) {
28         mv.apply {
29             visitVarInsn(Opcodes.ALOAD, variable)
30             // Stack: counters
31             push(edgeId)
32             // Stack: counters | edgeId
33             visitInsn(Opcodes.DUP2)
34             // Stack: counters | edgeId | counters | edgeId
35             visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/nio/ByteBuffer", "get", "(I)B", false)
36             // Increment the counter, but ensure that it never stays at 0 after an overflow by incrementing it again in
37             // that case.
38             // This approach performs better than saturating the counter at 255 (see Section 3.3 of
39             // https://www.usenix.org/system/files/woot20-paper-fioraldi.pdf)
40             // Stack: counters | edgeId | counter (sign-extended to int)
41             push(0xff)
42             // Stack: counters | edgeId | counter (sign-extended to int) | 0x000000ff
43             visitInsn(Opcodes.IAND)
44             // Stack: counters | edgeId | counter (zero-extended to int)
45             push(1)
46             // Stack: counters | edgeId | counter | 1
47             visitInsn(Opcodes.IADD)
48             // Stack: counters | edgeId | counter + 1
49             visitInsn(Opcodes.DUP)
50             // Stack: counters | edgeId | counter + 1 | counter + 1
51             push(8)
52             // Stack: counters | edgeId | counter + 1 | counter + 1 | 8 (maxStack: +5)
53             visitInsn(Opcodes.ISHR)
54             // Stack: counters | edgeId | counter + 1 | 1 if the increment overflowed to 0, 0 otherwise
55             visitInsn(Opcodes.IADD)
56             // Stack: counters | edgeId | counter + 2 if the increment overflowed, counter + 1 otherwise
57             visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/nio/ByteBuffer", "put", "(IB)Ljava/nio/ByteBuffer;", false)
58             // Stack: counters
59             visitInsn(Opcodes.POP)
60         }
61     }
62 
63     override val instrumentControlFlowEdgeStackSize = 5
64 
65     override val localVariableType get() = "java/nio/ByteBuffer"
66 
loadLocalVariablenull67     override fun loadLocalVariable(mv: MethodVisitor, variable: Int, coverageMapInternalClassName: String) {
68         mv.apply {
69             visitFieldInsn(
70                 Opcodes.GETSTATIC,
71                 coverageMapInternalClassName,
72                 "counters",
73                 "Ljava/nio/ByteBuffer;",
74             )
75             // Stack: counters (maxStack: 1)
76             visitVarInsn(Opcodes.ASTORE, variable)
77         }
78     }
79 
80     override val loadLocalVariableStackSize = 1
81 }
82