Skip to content

Commit 55152f9

Browse files
committed
Check tasty file hash
1 parent f3c8458 commit 55152f9

File tree

5 files changed

+52
-23
lines changed

5 files changed

+52
-23
lines changed

compiler/src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import scala.tools.asm
2626
import scala.tools.asm.tree._
2727
import tpd._
2828
import StdNames._
29+
import dotty.tools.dotc.core.tasty.TastyPickler
2930
import dotty.tools.io._
3031

3132
class GenBCode extends Phase {
@@ -221,9 +222,9 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
221222
val outstream = new DataOutputStream(outTastyFile.bufferedOutput)
222223
try outstream.write(binary)
223224
finally outstream.close()
224-
// TASTY attribute is created but 0 bytes are stored in it.
225-
// A TASTY attribute has length 0 if and only if the .tasty file exists.
226-
Array.empty[Byte]
225+
// TASTY attribute is created but only the header bytes are stored in it.
226+
// A TASTY attribute has length `headerSize` if and only if the .tasty file exists.
227+
java.util.Arrays.copyOf(binary, TastyPickler.headerSize)
227228
} else {
228229
// Create an empty file to signal that a tasty section exist in the corresponding .class
229230
// This is much cheaper and simpler to check than doing classfile parsing

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package classfile
66
import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
77
import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._
88
import NameKinds.{ModuleClassName, DefaultGetterName}
9+
import dotty.tools.dotc.core.tasty.{TastyHeaderUnpickler, TastyPickler}
910
import ast.tpd._
1011
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, File, IOException }
1112
import java.nio
@@ -785,7 +786,10 @@ class ClassfileParser(
785786

786787
if (scan(tpnme.TASTYATTR)) {
787788
val attrLen = in.nextInt
788-
if (attrLen == 0) { // A tasty attribute implies the existence of the .tasty file
789+
val bytes = in.nextBytes(attrLen)
790+
val headerUnpickler = new TastyHeaderUnpickler(bytes)
791+
headerUnpickler.readHeader()
792+
if (headerUnpickler.isAtEnd) { // A tasty attribute with that has only a header implies the existence of the .tasty file
789793
val tastyBytes: Array[Byte] = classfile.underlyingSource match { // TODO: simplify when #3552 is fixed
790794
case None =>
791795
ctx.error("Could not load TASTY from .tasty for virtual file " + classfile)
@@ -816,10 +820,13 @@ class ClassfileParser(
816820
Array.empty
817821
}
818822
}
819-
if (tastyBytes.nonEmpty)
823+
if (tastyBytes.nonEmpty) {
824+
if (!tastyBytes.startsWith(bytes))
825+
ctx.error("Header of TASTY file did not correspond to header in classfile. One of the files might be outdated or corrupted.")
820826
return unpickleTASTY(tastyBytes)
827+
}
821828
}
822-
else return unpickleTASTY(in.nextBytes(attrLen))
829+
else return unpickleTASTY(bytes)
823830
}
824831

825832
if (scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name)) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dotty.tools.dotc
2+
package core
3+
package tasty
4+
5+
import java.util.UUID
6+
7+
import dotty.tools.dotc.core.tasty.TastyFormat.{MajorVersion, MinorVersion, header}
8+
import dotty.tools.dotc.core.tasty.TastyUnpickler.UnpickleException
9+
10+
class TastyHeaderUnpickler(reader: TastyReader) {
11+
import reader._
12+
13+
def this(bytes: Array[Byte]) = this(new TastyReader(bytes))
14+
15+
def readHeader(): UUID = {
16+
for (i <- 0 until header.length)
17+
check(readByte() == header(i), "not a TASTy file")
18+
val major = readNat()
19+
val minor = readNat()
20+
check(major == MajorVersion && minor <= MinorVersion,
21+
s"""TASTy signature has wrong version.
22+
| expected: $MajorVersion.$MinorVersion
23+
| found : $major.$minor""".stripMargin)
24+
new UUID(readUncompressedLong(), readUncompressedLong())
25+
}
26+
27+
def isAtEnd: Boolean = reader.isAtEnd
28+
29+
private def check(cond: Boolean, msg: => String): Unit =
30+
if (!cond) throw new UnpickleException(msg)
31+
}

compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ class TastyPickler(val rootCls: ClassSymbol) {
2929
val uuidHi: Long = sections.iterator.map(x => pjwHash64(x._2.bytes)).fold(0L)(_ ^ _)
3030

3131
val headerBuffer = {
32-
val buf = new TastyBuffer(header.length + 24)
32+
val buf = new TastyBuffer(TastyPickler.headerSize)
3333
for (ch <- header) buf.writeByte(ch.toByte)
3434
buf.writeNat(MajorVersion)
3535
buf.writeNat(MinorVersion)
3636
buf.writeUncompressedLong(uuidLow)
3737
buf.writeUncompressedLong(uuidHi)
38+
assert(buf.length == TastyPickler.headerSize)
3839
buf
3940
}
4041

@@ -90,3 +91,7 @@ class TastyPickler(val rootCls: ClassSymbol) {
9091
h
9192
}
9293
}
94+
95+
object TastyPickler {
96+
val headerSize = header.length + 18
97+
}

compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ class TastyUnpickler(reader: TastyReader) {
3535
private val sectionReader = new mutable.HashMap[String, TastyReader]
3636
val nameAtRef = new NameTable
3737

38-
private def check(cond: Boolean, msg: => String) =
39-
if (!cond) throw new UnpickleException(msg)
40-
4138
private def readName(): TermName = nameAtRef(readNameRef())
4239
private def readString(): String = readName().toString
4340

@@ -73,19 +70,7 @@ class TastyUnpickler(reader: TastyReader) {
7370
result
7471
}
7572

76-
private def readHeader(): UUID = {
77-
for (i <- 0 until header.length)
78-
check(readByte() == header(i), "not a TASTy file")
79-
val major = readNat()
80-
val minor = readNat()
81-
check(major == MajorVersion && minor <= MinorVersion,
82-
s"""TASTy signature has wrong version.
83-
| expected: $MajorVersion.$MinorVersion
84-
| found : $major.$minor""".stripMargin)
85-
new UUID(readUncompressedLong(), readUncompressedLong())
86-
}
87-
88-
private val uuid = readHeader()
73+
new TastyHeaderUnpickler(reader).readHeader()
8974

9075
locally {
9176
until(readEnd()) { nameAtRef.add(readNameContents()) }

0 commit comments

Comments
 (0)