CVE-2017-15288
This commit is contained in:
parent
b8601533d7
commit
4380af6452
277
CVE-2017-15288-pre.patch
Normal file
277
CVE-2017-15288-pre.patch
Normal file
@ -0,0 +1,277 @@
|
||||
From caa9ebc482969a884da5f9c9c246470811b8599d Mon Sep 17 00:00:00 2001
|
||||
From: Teemu Lehtinen <teemu.t.lehtinen@aalto.fi>
|
||||
Date: Wed, 20 Aug 2014 13:20:41 +0300
|
||||
Subject: [PATCH] Add option -port to fsc
|
||||
|
||||
Option "port" limits compile server lookup and start to given port.
|
||||
Normally fsc will start a compile server in a random port if no server
|
||||
is yet running. This can be problematic with firewalls and/or remote
|
||||
compile servers. Option "port" should not be confused with option
|
||||
"server" which looks for a compile server in given host and port and
|
||||
fails if such server is not found.
|
||||
|
||||
Automatic tests for command line user interface do not exist at all.
|
||||
Thus, adding a test for one new option would require designing a whole
|
||||
new testing method.
|
||||
|
||||
Cherry picked from 7daecd8
|
||||
---
|
||||
.../scala/tools/nsc/CompileClient.scala | 4 +-
|
||||
.../scala/tools/nsc/CompileServer.scala | 56 ++++++++++++-------
|
||||
.../scala/tools/nsc/CompileSocket.scala | 37 +++++++-----
|
||||
.../tools/nsc/settings/FscSettings.scala | 4 +-
|
||||
.../scala/tools/util/SocketServer.scala | 4 +-
|
||||
5 files changed, 65 insertions(+), 40 deletions(-)
|
||||
|
||||
diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala
|
||||
index 731f6926f00..842d6ac535b 100644
|
||||
--- a/src/compiler/scala/tools/nsc/CompileClient.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/CompileClient.scala
|
||||
@@ -43,8 +43,8 @@ class StandardCompileClient extends HasCompileSocket with CompileOutputCommon {
|
||||
info(vmArgs.mkString("[VM arguments: ", " ", "]"))
|
||||
|
||||
val socket =
|
||||
- if (settings.server.value == "") compileSocket.getOrCreateSocket(vmArgs mkString " ", !shutdown)
|
||||
- else Some(compileSocket.getSocket(settings.server.value))
|
||||
+ if (settings.server.value == "") compileSocket.getOrCreateSocket(vmArgs mkString " ", !shutdown, settings.port.value)
|
||||
+ else compileSocket.getSocket(settings.server.value)
|
||||
|
||||
socket match {
|
||||
case Some(sock) => compileOnServer(sock, fscArgs)
|
||||
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
index 7a0a072bb8d..6352d75686a 100644
|
||||
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
package scala.tools.nsc
|
||||
|
||||
-import java.io.{ BufferedOutputStream, FileOutputStream, PrintStream }
|
||||
-import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
|
||||
-import scala.reflect.internal.util.FakePos //Position
|
||||
+import java.io.PrintStream
|
||||
+
|
||||
+import scala.reflect.internal.util.FakePos
|
||||
+import scala.tools.nsc.io.Directory
|
||||
+import scala.tools.nsc.reporters.{ConsoleReporter, Reporter}
|
||||
+import scala.tools.nsc.settings.FscSettings
|
||||
import scala.tools.util.SocketServer
|
||||
-import settings.FscSettings
|
||||
|
||||
/**
|
||||
* The server part of the fsc offline compiler. It awaits compilation
|
||||
@@ -19,7 +21,7 @@ import settings.FscSettings
|
||||
* @author Martin Odersky
|
||||
* @version 1.0
|
||||
*/
|
||||
-class StandardCompileServer extends SocketServer {
|
||||
+class StandardCompileServer(fixPort: Int = 0) extends SocketServer(fixPort) {
|
||||
lazy val compileSocket: CompileSocket = CompileSocket
|
||||
|
||||
private var compiler: Global = null
|
||||
@@ -34,7 +36,7 @@ class StandardCompileServer extends SocketServer {
|
||||
val MaxCharge = 0.8
|
||||
|
||||
private val runtime = Runtime.getRuntime()
|
||||
- import runtime.{ totalMemory, freeMemory, maxMemory }
|
||||
+ import runtime.{freeMemory, maxMemory, totalMemory}
|
||||
|
||||
/** Create a new compiler instance */
|
||||
def newGlobal(settings: Settings, reporter: Reporter) =
|
||||
@@ -170,16 +172,16 @@ class StandardCompileServer extends SocketServer {
|
||||
}
|
||||
|
||||
|
||||
-object CompileServer extends StandardCompileServer {
|
||||
+object CompileServer {
|
||||
/** A directory holding redirected output */
|
||||
- private lazy val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory()
|
||||
+ //private lazy val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory()
|
||||
|
||||
- private def createRedirect(filename: String) =
|
||||
- new PrintStream((redirectDir / filename).createFile().bufferedOutput())
|
||||
+ private def createRedirect(dir: Directory, filename: String) =
|
||||
+ new PrintStream((dir / filename).createFile().bufferedOutput())
|
||||
|
||||
- def main(args: Array[String]) =
|
||||
+ def main(args: Array[String]) =
|
||||
execute(() => (), args)
|
||||
-
|
||||
+
|
||||
/**
|
||||
* Used for internal testing. The callback is called upon
|
||||
* server start, notifying the caller that the server is
|
||||
@@ -191,21 +193,33 @@ object CompileServer extends StandardCompileServer {
|
||||
*/
|
||||
def execute(startupCallback : () => Unit, args: Array[String]) {
|
||||
val debug = args contains "-v"
|
||||
+ var port = 0
|
||||
+
|
||||
+ val i = args.indexOf("-p")
|
||||
+ if (i >= 0 && args.length > i + 1) {
|
||||
+ scala.util.control.Exception.ignoring(classOf[NumberFormatException]) {
|
||||
+ port = args(i + 1).toInt
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Create instance rather than extend to pass a port parameter.
|
||||
+ val server = new StandardCompileServer(port)
|
||||
+ val redirectDir = (server.compileSocket.tmpDir / "output-redirects").createDirectory()
|
||||
|
||||
if (debug) {
|
||||
- echo("Starting CompileServer on port " + port)
|
||||
- echo("Redirect dir is " + redirectDir)
|
||||
+ server.echo("Starting CompileServer on port " + server.port)
|
||||
+ server.echo("Redirect dir is " + redirectDir)
|
||||
}
|
||||
|
||||
- Console.withErr(createRedirect("scala-compile-server-err.log")) {
|
||||
- Console.withOut(createRedirect("scala-compile-server-out.log")) {
|
||||
- Console.err.println("...starting server on socket "+port+"...")
|
||||
+ Console.withErr(createRedirect(redirectDir, "scala-compile-server-err.log")) {
|
||||
+ Console.withOut(createRedirect(redirectDir, "scala-compile-server-out.log")) {
|
||||
+ Console.err.println("...starting server on socket "+server.port+"...")
|
||||
Console.err.flush()
|
||||
- compileSocket setPort port
|
||||
+ server.compileSocket setPort server.port
|
||||
startupCallback()
|
||||
- run()
|
||||
-
|
||||
- compileSocket deletePort port
|
||||
+ server.run()
|
||||
+
|
||||
+ server.compileSocket deletePort server.port
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
index 4051bda9144..f5039b8303f 100644
|
||||
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
@@ -5,16 +5,13 @@
|
||||
|
||||
package scala.tools.nsc
|
||||
|
||||
-import java.io.{ IOException, FileNotFoundException, PrintWriter, FileOutputStream }
|
||||
-import java.io.{ BufferedReader, FileReader }
|
||||
-import java.util.regex.Pattern
|
||||
-import java.net._
|
||||
+import java.io.FileNotFoundException
|
||||
import java.security.SecureRandom
|
||||
-import io.{ File, Path, Directory, Socket }
|
||||
-import scala.util.control.Exception.catching
|
||||
-import scala.tools.util.CompileOutputCommon
|
||||
+
|
||||
import scala.reflect.internal.util.StringOps.splitWhere
|
||||
import scala.sys.process._
|
||||
+import scala.tools.nsc.io.{File, Path, Socket}
|
||||
+import scala.tools.util.CompileOutputCommon
|
||||
|
||||
trait HasCompileSocket {
|
||||
def compileSocket: CompileSocket
|
||||
@@ -50,6 +47,9 @@ class CompileSocket extends CompileOutputCommon {
|
||||
protected lazy val compileClient: StandardCompileClient = CompileClient
|
||||
def verbose = compileClient.verbose
|
||||
|
||||
+ /* Fixes the port where to start the server, 0 yields some free port */
|
||||
+ var fixPort = 0
|
||||
+
|
||||
/** The prefix of the port identification file, which is followed
|
||||
* by the port number.
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ class CompileSocket extends CompileOutputCommon {
|
||||
|
||||
/** The class name of the scala compile server */
|
||||
protected val serverClass = "scala.tools.nsc.CompileServer"
|
||||
- protected def serverClassArgs = if (verbose) List("-v") else Nil // debug
|
||||
+ protected def serverClassArgs = (if (verbose) List("-v") else Nil) ::: (if (fixPort > 0) List("-p", fixPort.toString) else Nil)
|
||||
|
||||
/** A temporary directory to use */
|
||||
val tmpDir = {
|
||||
@@ -107,9 +107,14 @@ class CompileSocket extends CompileOutputCommon {
|
||||
def portFile(port: Int) = portsDir / File(port.toString)
|
||||
|
||||
/** Poll for a server port number; return -1 if none exists yet */
|
||||
- private def pollPort(): Int = portsDir.list.toList match {
|
||||
+ private def pollPort(): Int = if (fixPort > 0) {
|
||||
+ if (portsDir.list.toList.exists(_.name == fixPort.toString)) fixPort else -1
|
||||
+ } else portsDir.list.toList match {
|
||||
case Nil => -1
|
||||
- case x :: xs => try x.name.toInt finally xs foreach (_.delete())
|
||||
+ case x :: xs => try x.name.toInt catch {
|
||||
+ case e: Exception => x.delete()
|
||||
+ throw e
|
||||
+ }
|
||||
}
|
||||
|
||||
/** Get the port number to which a scala compile server is connected;
|
||||
@@ -155,7 +160,8 @@ class CompileSocket extends CompileOutputCommon {
|
||||
* create a new daemon if necessary. Returns None if the connection
|
||||
* cannot be established.
|
||||
*/
|
||||
- def getOrCreateSocket(vmArgs: String, create: Boolean = true): Option[Socket] = {
|
||||
+ def getOrCreateSocket(vmArgs: String, create: Boolean = true, fixedPort: Int = 0): Option[Socket] = {
|
||||
+ fixPort = fixedPort
|
||||
val maxMillis = 10 * 1000 // try for 10 seconds
|
||||
val retryDelay = 50
|
||||
val maxAttempts = maxMillis / retryDelay
|
||||
@@ -189,13 +195,16 @@ class CompileSocket extends CompileOutputCommon {
|
||||
try { Some(x.toInt) }
|
||||
catch { case _: NumberFormatException => None }
|
||||
|
||||
- def getSocket(serverAdr: String): Socket = (
|
||||
+ def getSocket(serverAdr: String): Option[Socket] = (
|
||||
for ((name, portStr) <- splitWhere(serverAdr, _ == ':', true) ; port <- parseInt(portStr)) yield
|
||||
getSocket(name, port)
|
||||
) getOrElse fatal("Malformed server address: %s; exiting" format serverAdr)
|
||||
|
||||
- def getSocket(hostName: String, port: Int): Socket =
|
||||
- Socket(hostName, port).opt getOrElse fatal("Unable to establish connection to server %s:%d; exiting".format(hostName, port))
|
||||
+ def getSocket(hostName: String, port: Int): Option[Socket] = {
|
||||
+ val sock = Socket(hostName, port).opt
|
||||
+ if (sock.isEmpty) warn("Unable to establish connection to server %s:%d".format(hostName, port))
|
||||
+ sock
|
||||
+ }
|
||||
|
||||
def getPassword(port: Int): String = {
|
||||
val ff = portFile(port)
|
||||
diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
||||
index 5c852ae07c1..f5f971d697e 100644
|
||||
--- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
||||
@@ -22,13 +22,15 @@ class FscSettings(error: String => Unit) extends Settings(error) {
|
||||
val reset = BooleanSetting("-reset", "Reset compile server caches")
|
||||
val shutdown = BooleanSetting("-shutdown", "Shutdown compile server")
|
||||
val server = StringSetting ("-server", "hostname:portnumber", "Specify compile server socket", "")
|
||||
+ val port = IntSetting ("-port", "Search and start compile server in given port only",
|
||||
+ 0, Some((0, Int.MaxValue)), (_: String) => None)
|
||||
val preferIPv4 = BooleanSetting("-ipv4", "Use IPv4 rather than IPv6 for the server socket")
|
||||
val idleMins = IntSetting ("-max-idle", "Set idle timeout in minutes for fsc (use 0 for no timeout)",
|
||||
30, Some((0, Int.MaxValue)), (_: String) => None)
|
||||
|
||||
// For improved help output, separating fsc options from the others.
|
||||
def fscSpecific = Set[Settings#Setting](
|
||||
- currentDir, reset, shutdown, server, preferIPv4, idleMins
|
||||
+ currentDir, reset, shutdown, server, port, preferIPv4, idleMins
|
||||
)
|
||||
val isFscSpecific: String => Boolean = fscSpecific map (_.name)
|
||||
|
||||
diff --git a/src/compiler/scala/tools/util/SocketServer.scala b/src/compiler/scala/tools/util/SocketServer.scala
|
||||
index 1b06ce2ff2e..edbc7ecc554 100644
|
||||
--- a/src/compiler/scala/tools/util/SocketServer.scala
|
||||
+++ b/src/compiler/scala/tools/util/SocketServer.scala
|
||||
@@ -27,12 +27,12 @@ trait CompileOutputCommon {
|
||||
* @author Martin Odersky
|
||||
* @version 1.0
|
||||
*/
|
||||
-abstract class SocketServer extends CompileOutputCommon {
|
||||
+abstract class SocketServer(fixPort: Int = 0) extends CompileOutputCommon {
|
||||
def shutdown: Boolean
|
||||
def session(): Unit
|
||||
def timeout(): Unit = () // called after a timeout is detected for subclasses to cleanup
|
||||
// a hook for subclasses
|
||||
- protected def createServerSocket(): ServerSocket = new ServerSocket(0)
|
||||
+ protected def createServerSocket(): ServerSocket = new ServerSocket(fixPort)
|
||||
|
||||
var in: BufferedReader = _
|
||||
var out: PrintWriter = _
|
||||
506
CVE-2017-15288.patch
Normal file
506
CVE-2017-15288.patch
Normal file
@ -0,0 +1,506 @@
|
||||
From 67e1437e55df6789d0883cb8846d12071de75c63 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Zaugg <jzaugg@gmail.com>
|
||||
Date: Mon, 2 Oct 2017 10:06:55 +1000
|
||||
Subject: [PATCH] Move compilation daemon portfile under `~/.scalac/`
|
||||
|
||||
Store the compilation daemon's administrativia (port file, redirection)
|
||||
under `~/.scalac/`, instead of the less standard
|
||||
`/tmp/scala-devel/${USER:shared}/scalac-compile-server-port`.
|
||||
|
||||
On creation, remove group- and other-permissions from these
|
||||
private files, ditto for the repl's history file.
|
||||
|
||||
On Java 6 on Windows, opt in to compilation daemon using `-nc:false`.
|
||||
|
||||
Cherry picked from b64ad85, aa133c9, 2ceb09c
|
||||
---
|
||||
.../scala/tools/nsc/CompileServer.scala | 22 ++--
|
||||
.../scala/tools/nsc/CompileSocket.scala | 68 ++++++-----
|
||||
.../tools/nsc/GenericRunnerSettings.scala | 5 +-
|
||||
src/compiler/scala/tools/nsc/Properties.scala | 5 +
|
||||
.../scala/tools/nsc/ScriptRunner.scala | 20 +++-
|
||||
.../session/FileBackedHistory.scala | 32 +++++-
|
||||
.../tools/nsc/util/ScalaClassLoader.scala | 27 ++---
|
||||
.../internal/util/OwnerOnlyChmod.scala | 107 ++++++++++++++++++
|
||||
8 files changed, 221 insertions(+), 65 deletions(-)
|
||||
create mode 100644 src/reflect/scala/reflect/internal/util/OwnerOnlyChmod.scala
|
||||
|
||||
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
index 6352d75686a..c454ba8b62b 100644
|
||||
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
|
||||
@@ -183,14 +183,15 @@ object CompileServer {
|
||||
execute(() => (), args)
|
||||
|
||||
/**
|
||||
- * Used for internal testing. The callback is called upon
|
||||
- * server start, notifying the caller that the server is
|
||||
- * ready to run. WARNING: the callback runs in the
|
||||
- * server's thread, blocking the server from doing any work
|
||||
- * until the callback is finished. Callbacks should be kept
|
||||
- * simple and clients should not try to interact with the
|
||||
- * server while the callback is processing.
|
||||
- */
|
||||
+ * The server's main loop.
|
||||
+ *
|
||||
+ * `startupCallback` is used for internal testing; it's called upon server start,
|
||||
+ * notifying the caller that the server is ready to run.
|
||||
+ *
|
||||
+ * WARNING: the callback runs in the server's thread, blocking the server from doing any work
|
||||
+ * until the callback is finished. Callbacks should be kept simple and clients should not try to
|
||||
+ * interact with the server while the callback is processing.
|
||||
+ */
|
||||
def execute(startupCallback : () => Unit, args: Array[String]) {
|
||||
val debug = args contains "-v"
|
||||
var port = 0
|
||||
@@ -198,14 +199,13 @@ object CompileServer {
|
||||
val i = args.indexOf("-p")
|
||||
if (i >= 0 && args.length > i + 1) {
|
||||
scala.util.control.Exception.ignoring(classOf[NumberFormatException]) {
|
||||
- port = args(i + 1).toInt
|
||||
+ port = args(i + 1).toInt
|
||||
}
|
||||
}
|
||||
|
||||
// Create instance rather than extend to pass a port parameter.
|
||||
val server = new StandardCompileServer(port)
|
||||
- val redirectDir = (server.compileSocket.tmpDir / "output-redirects").createDirectory()
|
||||
-
|
||||
+ val redirectDir = server.compileSocket.mkDaemonDir("fsc_redirects")
|
||||
if (debug) {
|
||||
server.echo("Starting CompileServer on port " + server.port)
|
||||
server.echo("Redirect dir is " + redirectDir)
|
||||
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
index f5039b8303f..b73d251e9cc 100644
|
||||
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
||||
@@ -5,13 +5,17 @@
|
||||
|
||||
package scala.tools.nsc
|
||||
|
||||
-import java.io.FileNotFoundException
|
||||
+import java.math.BigInteger
|
||||
import java.security.SecureRandom
|
||||
|
||||
+import scala.io.Codec
|
||||
+import scala.reflect.internal.util.OwnerOnlyChmod
|
||||
import scala.reflect.internal.util.StringOps.splitWhere
|
||||
import scala.sys.process._
|
||||
-import scala.tools.nsc.io.{File, Path, Socket}
|
||||
+import scala.tools.nsc.Properties.scalacDir
|
||||
+import scala.tools.nsc.io.{File, Socket}
|
||||
import scala.tools.util.CompileOutputCommon
|
||||
+import scala.util.control.NonFatal
|
||||
|
||||
trait HasCompileSocket {
|
||||
def compileSocket: CompileSocket
|
||||
@@ -46,14 +50,10 @@ trait HasCompileSocket {
|
||||
class CompileSocket extends CompileOutputCommon {
|
||||
protected lazy val compileClient: StandardCompileClient = CompileClient
|
||||
def verbose = compileClient.verbose
|
||||
-
|
||||
+ def verbose_=(v: Boolean) = compileClient.verbose = v
|
||||
/* Fixes the port where to start the server, 0 yields some free port */
|
||||
var fixPort = 0
|
||||
|
||||
- /** The prefix of the port identification file, which is followed
|
||||
- * by the port number.
|
||||
- */
|
||||
- protected lazy val dirName = "scalac-compile-server-port"
|
||||
protected def cmdName = Properties.scalaCmd
|
||||
|
||||
/** The vm part of the command to start a new scala compile server */
|
||||
@@ -69,20 +69,8 @@ class CompileSocket extends CompileOutputCommon {
|
||||
protected val serverClass = "scala.tools.nsc.CompileServer"
|
||||
protected def serverClassArgs = (if (verbose) List("-v") else Nil) ::: (if (fixPort > 0) List("-p", fixPort.toString) else Nil)
|
||||
|
||||
- /** A temporary directory to use */
|
||||
- val tmpDir = {
|
||||
- val udir = Option(Properties.userName) getOrElse "shared"
|
||||
- val f = (Path(Properties.tmpDir) / ("scala-devel" + udir)).createDirectory()
|
||||
-
|
||||
- if (f.isDirectory && f.canWrite) {
|
||||
- info("[Temp directory: " + f + "]")
|
||||
- f
|
||||
- }
|
||||
- else fatal("Could not find a directory for temporary files")
|
||||
- }
|
||||
-
|
||||
/* A directory holding port identification files */
|
||||
- val portsDir = (tmpDir / dirName).createDirectory()
|
||||
+ private lazy val portsDir = mkDaemonDir("fsc_port")
|
||||
|
||||
/** The command which starts the compile server, given vm arguments.
|
||||
*
|
||||
@@ -104,7 +92,7 @@ class CompileSocket extends CompileOutputCommon {
|
||||
}
|
||||
|
||||
/** The port identification file */
|
||||
- def portFile(port: Int) = portsDir / File(port.toString)
|
||||
+ def portFile(port: Int): File = portsDir / File(port.toString)
|
||||
|
||||
/** Poll for a server port number; return -1 if none exists yet */
|
||||
private def pollPort(): Int = if (fixPort > 0) {
|
||||
@@ -138,19 +126,19 @@ class CompileSocket extends CompileOutputCommon {
|
||||
}
|
||||
info("[Port number: " + port + "]")
|
||||
if (port < 0)
|
||||
- fatal("Could not connect to compilation daemon after " + attempts + " attempts.")
|
||||
+ fatal(s"Could not connect to compilation daemon after $attempts attempts. To run without it, use `-nocompdaemon` or `-nc`.")
|
||||
port
|
||||
}
|
||||
|
||||
/** Set the port number to which a scala compile server is connected */
|
||||
- def setPort(port: Int) {
|
||||
- val file = portFile(port)
|
||||
- val secret = new SecureRandom().nextInt.toString
|
||||
-
|
||||
- try file writeAll secret catch {
|
||||
- case e @ (_: FileNotFoundException | _: SecurityException) =>
|
||||
- fatal("Cannot create file: %s".format(file.path))
|
||||
- }
|
||||
+ def setPort(port: Int): Unit = {
|
||||
+ val file = portFile(port)
|
||||
+ // 128 bits of delicious randomness, suitable for printing with println over a socket,
|
||||
+ // and storage in a file -- see getPassword
|
||||
+ val secretDigits = new BigInteger(128, new SecureRandom()).toString.getBytes("UTF-8")
|
||||
+
|
||||
+ try OwnerOnlyChmod().chmodAndWrite(file.jfile, secretDigits)
|
||||
+ catch chmodFailHandler(s"Cannot create file: ${file}")
|
||||
}
|
||||
|
||||
/** Delete the port number to which a scala compile server was connected */
|
||||
@@ -208,7 +196,7 @@ class CompileSocket extends CompileOutputCommon {
|
||||
|
||||
def getPassword(port: Int): String = {
|
||||
val ff = portFile(port)
|
||||
- val f = ff.bufferedReader()
|
||||
+ val f = ff.bufferedReader(Codec.UTF8)
|
||||
|
||||
// allow some time for the server to start up
|
||||
def check = {
|
||||
@@ -223,6 +211,24 @@ class CompileSocket extends CompileOutputCommon {
|
||||
f.close()
|
||||
result
|
||||
}
|
||||
+
|
||||
+ private def chmodFailHandler(msg: String): PartialFunction[Throwable, Unit] = {
|
||||
+ case NonFatal(e) =>
|
||||
+ if (verbose) e.printStackTrace()
|
||||
+ fatal(msg)
|
||||
+ }
|
||||
+
|
||||
+ def mkDaemonDir(name: String) = {
|
||||
+ val dir = (scalacDir / name).createDirectory()
|
||||
+
|
||||
+ if (dir.isDirectory && dir.canWrite) info(s"[Temp directory: $dir]")
|
||||
+ else fatal(s"Could not create compilation daemon directory $dir")
|
||||
+
|
||||
+ try OwnerOnlyChmod().chmod(dir.jfile)
|
||||
+ catch chmodFailHandler(s"Failed to change permissions on $dir. The compilation daemon requires a secure directory; use -nc to disable the daemon.")
|
||||
+ dir
|
||||
+ }
|
||||
+
|
||||
}
|
||||
|
||||
|
||||
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
|
||||
index 9c2db11a56e..edfc095c7f7 100644
|
||||
--- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
|
||||
@@ -38,8 +38,11 @@ class GenericRunnerSettings(error: String => Unit) extends Settings(error) {
|
||||
|
||||
val nc = BooleanSetting(
|
||||
"-nc",
|
||||
- "do not use the fsc compilation daemon") withAbbreviation "-nocompdaemon"
|
||||
+ "do not use the fsc compilation daemon") withAbbreviation "-nocompdaemon" withPostSetHook((x: BooleanSetting) => {_useCompDaemon = !x.value })
|
||||
|
||||
@deprecated("Use `nc` instead", "2.9.0") def nocompdaemon = nc
|
||||
@deprecated("Use `save` instead", "2.9.0") def savecompiled = save
|
||||
+
|
||||
+ private[this] var _useCompDaemon = true
|
||||
+ def useCompDaemon: Boolean = _useCompDaemon
|
||||
}
|
||||
diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala
|
||||
index 55fd1967164..8b314ba0b82 100644
|
||||
--- a/src/compiler/scala/tools/nsc/Properties.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/Properties.scala
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package scala.tools.nsc
|
||||
|
||||
+import scala.tools.nsc.io.Path
|
||||
+
|
||||
/** Loads `compiler.properties` from the jar archive file.
|
||||
*/
|
||||
object Properties extends scala.util.PropertiesTrait {
|
||||
@@ -22,4 +24,7 @@ object Properties extends scala.util.PropertiesTrait {
|
||||
// derived values
|
||||
def isEmacsShell = propOrEmpty("env.emacs") != ""
|
||||
def fileEndings = fileEndingString.split("""\|""").toList
|
||||
+
|
||||
+ // Where we keep fsc's state (ports/redirection)
|
||||
+ lazy val scalacDir = (Path(Properties.userHome) / ".scalac").createDirectory(force = false)
|
||||
}
|
||||
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
|
||||
index 107c4b3df3d..9af0079ffd6 100644
|
||||
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
|
||||
@@ -77,7 +77,10 @@ class ScriptRunner extends HasCompileSocket {
|
||||
val coreCompArgs = compSettings flatMap (_.unparse)
|
||||
val compArgs = coreCompArgs ++ List("-Xscript", scriptMain(settings), scriptFile)
|
||||
|
||||
- CompileSocket getOrCreateSocket "" match {
|
||||
+ // TODO: untangle this mess of top-level objects with their own little view of the mutable world of settings
|
||||
+ compileSocket.verbose = settings.verbose.value
|
||||
+
|
||||
+ compileSocket getOrCreateSocket "" match {
|
||||
case Some(sock) => compileOnServer(sock, compArgs)
|
||||
case _ => false
|
||||
}
|
||||
@@ -109,14 +112,23 @@ class ScriptRunner extends HasCompileSocket {
|
||||
|
||||
settings.outdir.value = compiledPath.path
|
||||
|
||||
- if (settings.nc.value) {
|
||||
- /** Setting settings.script.value informs the compiler this is not a
|
||||
- * self contained compilation unit.
|
||||
+ // can't reliably lock down permissions on the portfile in this environment => disable by default.
|
||||
+ // not the cleanest to do this here, but I don't see where else to decide this and emit the warning below
|
||||
+ val cantLockdown = !settings.nc.isSetByUser && scala.util.Properties.isWin && !scala.util.Properties.isJavaAtLeast("7")
|
||||
+
|
||||
+ if (cantLockdown) settings.nc.value = true
|
||||
+
|
||||
+ if (!settings.useCompDaemon) {
|
||||
+ /* Setting settings.script.value informs the compiler this is not a
|
||||
+ * self contained compilation unit.
|
||||
*/
|
||||
settings.script.value = mainClass
|
||||
val reporter = new ConsoleReporter(settings)
|
||||
val compiler = newGlobal(settings, reporter)
|
||||
|
||||
+ if (cantLockdown)
|
||||
+ reporter.echo("[info] The compilation daemon is disabled by default on this platform. To force its usage, use `-nocompdaemon:false`.")
|
||||
+
|
||||
new compiler.Run compile List(scriptFile)
|
||||
if (reporter.hasErrors) None else Some(compiledPath)
|
||||
}
|
||||
diff --git a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
|
||||
index dddfb1b8f64..5467c0a61ef 100644
|
||||
--- a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
|
||||
@@ -7,14 +7,37 @@ package scala.tools.nsc
|
||||
package interpreter
|
||||
package session
|
||||
|
||||
-import scala.tools.nsc.io._
|
||||
-import FileBackedHistory._
|
||||
+import scala.reflect.internal.util.OwnerOnlyChmod
|
||||
+import scala.reflect.io.{File, Path}
|
||||
+import scala.tools.nsc.Properties.{propOrNone, userHome}
|
||||
+import scala.util.control.NonFatal
|
||||
|
||||
/** TODO: file locking.
|
||||
*/
|
||||
trait FileBackedHistory extends JLineHistory with JPersistentHistory {
|
||||
def maxSize: Int = 2500
|
||||
- protected lazy val historyFile: File = defaultFile
|
||||
+
|
||||
+ // For a history file in the standard location, always try to restrict permission,
|
||||
+ // creating an empty file if none exists.
|
||||
+ // For a user-specified location, only lock down permissions if we're the ones
|
||||
+ // creating it, otherwise responsibility for permissions is up to the caller.
|
||||
+ protected lazy val historyFile: File = File {
|
||||
+ propOrNone("scala.shell.histfile").map(Path.apply) match {
|
||||
+ case Some(p) => if (!p.exists) secure(p) else p
|
||||
+ case None => secure(Path(userHome) / FileBackedHistory.defaultFileName)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private def secure(p: Path): Path = {
|
||||
+ try OwnerOnlyChmod().chmodOrCreateEmpty(p.jfile)
|
||||
+ catch { case NonFatal(e) =>
|
||||
+ if (interpreter.isReplDebug) e.printStackTrace()
|
||||
+ interpreter.replinfo(s"Warning: history file ${p}'s permissions could not be restricted to owner-only.")
|
||||
+ }
|
||||
+
|
||||
+ p
|
||||
+ }
|
||||
+
|
||||
private var isPersistent = true
|
||||
|
||||
locally {
|
||||
@@ -79,6 +102,5 @@ object FileBackedHistory {
|
||||
// val ContinuationNL: String = Array('\003', '\n').mkString
|
||||
import Properties.userHome
|
||||
|
||||
- def defaultFileName = ".scala_history"
|
||||
- def defaultFile: File = File(Path(userHome) / defaultFileName)
|
||||
+ final val defaultFileName = ".scala_history"
|
||||
}
|
||||
diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
|
||||
index 1f6fa68f572..0673fa1f758 100644
|
||||
--- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
|
||||
+++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
|
||||
@@ -3,19 +3,18 @@
|
||||
* @author Paul Phillips
|
||||
*/
|
||||
|
||||
-package scala.tools.nsc
|
||||
-package util
|
||||
-
|
||||
-import java.lang.{ ClassLoader => JClassLoader }
|
||||
-import java.lang.reflect.{ Constructor, Modifier, Method }
|
||||
-import java.io.{ File => JFile }
|
||||
-import java.net.{ URLClassLoader => JURLClassLoader }
|
||||
-import java.net.URL
|
||||
-import scala.reflect.runtime.ReflectionUtils.unwrapHandler
|
||||
-import ScalaClassLoader._
|
||||
-import scala.util.control.Exception.{ catching }
|
||||
+package scala.tools.nsc.util
|
||||
+
|
||||
+import java.io.{File => JFile}
|
||||
+import java.lang.reflect.{Constructor, Modifier}
|
||||
+import java.lang.{ClassLoader => JClassLoader}
|
||||
+import java.net.{URL, URLClassLoader => JURLClassLoader}
|
||||
+
|
||||
import scala.language.implicitConversions
|
||||
-import scala.reflect.{ ClassTag, classTag }
|
||||
+import scala.reflect.runtime.ReflectionUtils.unwrapHandler
|
||||
+import scala.reflect.{ClassTag, classTag}
|
||||
+import scala.tools.nsc.io.Streamable
|
||||
+import scala.util.control.Exception.catching
|
||||
|
||||
trait HasClassPath {
|
||||
def classPathURLs: Seq[URL]
|
||||
@@ -25,6 +24,8 @@ trait HasClassPath {
|
||||
* of java reflection.
|
||||
*/
|
||||
trait ScalaClassLoader extends JClassLoader {
|
||||
+ import ScalaClassLoader._
|
||||
+
|
||||
/** Executing an action with this classloader as context classloader */
|
||||
def asContext[T](action: => T): T = {
|
||||
val saved = contextLoader
|
||||
@@ -52,7 +53,7 @@ trait ScalaClassLoader extends JClassLoader {
|
||||
/** The actual bytes for a class file, or an empty array if it can't be found. */
|
||||
def classBytes(className: String): Array[Byte] = classAsStream(className) match {
|
||||
case null => Array()
|
||||
- case stream => io.Streamable.bytes(stream)
|
||||
+ case stream => Streamable.bytes(stream)
|
||||
}
|
||||
|
||||
/** An InputStream representing the given class name, or null if not found. */
|
||||
diff --git a/src/reflect/scala/reflect/internal/util/OwnerOnlyChmod.scala b/src/reflect/scala/reflect/internal/util/OwnerOnlyChmod.scala
|
||||
new file mode 100644
|
||||
index 00000000000..c0da65db387
|
||||
--- /dev/null
|
||||
+++ b/src/reflect/scala/reflect/internal/util/OwnerOnlyChmod.scala
|
||||
@@ -0,0 +1,107 @@
|
||||
+/* NSC -- new Scala compiler
|
||||
+ * Copyright 2017 LAMP/EPFL
|
||||
+ * @author Martin Odersky
|
||||
+ */
|
||||
+package scala.reflect.internal.util
|
||||
+
|
||||
+import java.io.{File, FileOutputStream, IOException}
|
||||
+
|
||||
+
|
||||
+trait OwnerOnlyChmod {
|
||||
+ /** Remove group/other permissions for `file`, it if exists */
|
||||
+ def chmod(file: java.io.File): Unit
|
||||
+
|
||||
+ /** Delete `file` if it exists, recreate it with no group/other permissions, and write `contents` */
|
||||
+ final def chmodAndWrite(file: File, contents: Array[Byte]): Unit = {
|
||||
+ file.delete()
|
||||
+ val fos = new FileOutputStream(file)
|
||||
+ fos.close()
|
||||
+ chmod(file)
|
||||
+ val fos2 = new FileOutputStream(file)
|
||||
+ try {
|
||||
+ fos2.write(contents)
|
||||
+ } finally {
|
||||
+ fos2.close()
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // TODO: use appropriate NIO call instead of two-step exists?/create!
|
||||
+ final def chmodOrCreateEmpty(file: File): Unit =
|
||||
+ if (!file.exists()) chmodAndWrite(file, Array[Byte]()) else chmod(file)
|
||||
+
|
||||
+}
|
||||
+
|
||||
+object OwnerOnlyChmod {
|
||||
+ def apply(): OwnerOnlyChmod = {
|
||||
+ if (!util.Properties.isWin) Java6UnixChmod
|
||||
+ else if (util.Properties.isJavaAtLeast("7")) new NioAclChmodReflective
|
||||
+ else NoOpOwnerOnlyChmod
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+object NoOpOwnerOnlyChmod extends OwnerOnlyChmod {
|
||||
+ override def chmod(file: File): Unit = ()
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/** Adjust permissions with `File.{setReadable, setWritable}` */
|
||||
+object Java6UnixChmod extends OwnerOnlyChmod {
|
||||
+
|
||||
+ def chmod(file: File): Unit = if (file.exists()) {
|
||||
+ def clearAndSetOwnerOnly(f: (Boolean, Boolean) => Boolean): Unit = {
|
||||
+ def fail() = throw new IOException("Unable to modify permissions of " + file)
|
||||
+ // attribute = false, ownerOnly = false
|
||||
+ if (!f(false, false)) fail()
|
||||
+ // attribute = true, ownerOnly = true
|
||||
+ if (!f(true, true)) fail()
|
||||
+ }
|
||||
+ if (file.isDirectory) {
|
||||
+ clearAndSetOwnerOnly(file.setExecutable)
|
||||
+ }
|
||||
+ clearAndSetOwnerOnly(file.setReadable)
|
||||
+ clearAndSetOwnerOnly(file.setWritable)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+object NioAclChmodReflective {
|
||||
+ val file_toPath = classOf[java.io.File].getMethod("toPath")
|
||||
+ val files = Class.forName("java.nio.file.Files")
|
||||
+ val path_class = Class.forName("java.nio.file.Path")
|
||||
+ val getFileAttributeView = files.getMethod("getFileAttributeView", path_class, classOf[Class[_]], Class.forName("[Ljava.nio.file.LinkOption;"))
|
||||
+ val linkOptionEmptyArray = java.lang.reflect.Array.newInstance(Class.forName("java.nio.file.LinkOption"), 0)
|
||||
+ val aclFileAttributeView_class = Class.forName("java.nio.file.attribute.AclFileAttributeView")
|
||||
+ val aclEntry_class = Class.forName("java.nio.file.attribute.AclEntry")
|
||||
+ val aclEntryBuilder_class = Class.forName("java.nio.file.attribute.AclEntry$Builder")
|
||||
+ val newBuilder = aclEntry_class.getMethod("newBuilder")
|
||||
+ val aclEntryBuilder_build = aclEntryBuilder_class.getMethod("build")
|
||||
+ val userPrinciple_class = Class.forName("java.nio.file.attribute.UserPrincipal")
|
||||
+ val setPrincipal = aclEntryBuilder_class.getMethod("setPrincipal", userPrinciple_class)
|
||||
+ val setPermissions = aclEntryBuilder_class.getMethod("setPermissions", Class.forName("[Ljava.nio.file.attribute.AclEntryPermission;"))
|
||||
+ val aclEntryType_class = Class.forName("java.nio.file.attribute.AclEntryType")
|
||||
+ val setType = aclEntryBuilder_class.getMethod("setType", aclEntryType_class)
|
||||
+ val aclEntryPermission_class = Class.forName("java.nio.file.attribute.AclEntryPermission")
|
||||
+ val aclEntryPermissionValues = aclEntryPermission_class.getDeclaredMethod("values")
|
||||
+ val aclEntryType_ALLOW = aclEntryType_class.getDeclaredField("ALLOW")
|
||||
+}
|
||||
+
|
||||
+/** Reflective version of `NioAclChmod` */
|
||||
+final class NioAclChmodReflective extends OwnerOnlyChmod {
|
||||
+ import NioAclChmodReflective._
|
||||
+ def chmod(file: java.io.File): Unit = {
|
||||
+ val path = file_toPath.invoke(file)
|
||||
+ val view = getFileAttributeView.invoke(null, path, aclFileAttributeView_class, linkOptionEmptyArray)
|
||||
+ val setAcl = aclFileAttributeView_class.getMethod("setAcl", classOf[java.util.List[_]])
|
||||
+ val getOwner = aclFileAttributeView_class.getMethod("getOwner")
|
||||
+ val owner = getOwner.invoke(view)
|
||||
+ setAcl.invoke(view, acls(owner))
|
||||
+ }
|
||||
+
|
||||
+ private def acls(owner: Object) = {
|
||||
+ val builder = newBuilder.invoke(null)
|
||||
+ setPrincipal.invoke(builder, owner)
|
||||
+ setPermissions.invoke(builder, aclEntryPermissionValues.invoke(null))
|
||||
+ setType.invoke(builder, aclEntryType_ALLOW.get(null))
|
||||
+ java.util.Collections.singletonList(aclEntryBuilder_build.invoke(builder))
|
||||
+ }
|
||||
+}
|
||||
10
scala.spec
10
scala.spec
@ -3,7 +3,7 @@
|
||||
|
||||
Name: scala
|
||||
Version: 2.10.6
|
||||
Release: 12
|
||||
Release: 13
|
||||
Summary: Combination of object-oriented and functional programming
|
||||
License: BSD and CC0 and Public Domain
|
||||
URL: http://www.scala-lang.org/
|
||||
@ -24,6 +24,8 @@ Patch2: scala-2.10.3-compiler-pom.patch
|
||||
Patch3: scala-2.10.2-java7.patch
|
||||
Patch4: scala-2.10-jline.patch
|
||||
Patch5: scala-2.10.4-build_xml.patch
|
||||
Patch6000: CVE-2017-15288-pre.patch
|
||||
Patch6001: CVE-2017-15288.patch
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: java-devel >= 1:1.7.0, ant, ant-junit, ant-contrib, jline >= 2.10, aqute-bnd, junit, javapackages-local, scala
|
||||
@ -156,5 +158,11 @@ update-mime-database %{?fedora:-n} %{_datadir}/mime > /dev/null 2>&1 || :
|
||||
/usr/share/maven*
|
||||
|
||||
%changelog
|
||||
* Thu Dec 26 2019 zhujunhao <zhujunhao5@huawei.com> - 2.10.6-13
|
||||
- Type:cves
|
||||
- ID:CVE-2017-15288
|
||||
- SUG:restart
|
||||
- DESC:fix CVE-2017-15288
|
||||
|
||||
* Fri Dec 13 2019 openEuler Buildteam <buildteam@openeuler.org> - 2.10.6-12
|
||||
- Package init
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user