!3 fix CVE-2017-15288

Merge pull request !3 from compile_success/master
This commit is contained in:
openeuler-ci-bot 2019-12-26 16:59:29 +08:00 committed by Gitee
commit 067e38bbb2
3 changed files with 792 additions and 1 deletions

277
CVE-2017-15288-pre.patch Normal file
View 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
View 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))
+ }
+}

View File

@ -3,7 +3,7 @@
Name: scala Name: scala
Version: 2.10.6 Version: 2.10.6
Release: 12 Release: 13
Summary: Combination of object-oriented and functional programming Summary: Combination of object-oriented and functional programming
License: BSD and CC0 and Public Domain License: BSD and CC0 and Public Domain
URL: http://www.scala-lang.org/ 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 Patch3: scala-2.10.2-java7.patch
Patch4: scala-2.10-jline.patch Patch4: scala-2.10-jline.patch
Patch5: scala-2.10.4-build_xml.patch Patch5: scala-2.10.4-build_xml.patch
Patch6000: CVE-2017-15288-pre.patch
Patch6001: CVE-2017-15288.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: java-devel >= 1:1.7.0, ant, ant-junit, ant-contrib, jline >= 2.10, aqute-bnd, junit, javapackages-local, scala 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* /usr/share/maven*
%changelog %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 * Fri Dec 13 2019 openEuler Buildteam <buildteam@openeuler.org> - 2.10.6-12
- Package init - Package init