-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
5.0
-
Cause Known
-
x86
-
linux
FULL PRODUCT VERSION :
java version "1.5.0_03"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_03-b07)
Java HotSpot(TM) Client VM (build 1.5.0_03-b07, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Linux bertha 2.6.8-1-686-smp #1 SMP Thu Nov 25 04:55:00 UTC 2004 i686 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
Robot contains the following check on Rectangles passed to createScreenCapture:
private static void checkValidRect(Rectangle rect) {
if (rect.width <= 0 || rect.height <= 0) {
throw new IllegalArgumentException("Rectangle width and height must be > 0");
}
}
on Mac OS, if rect.y < 0, the Java VM crashes.
on all platforms, values of rect.x or rect.y less than 0 give undefined results, which doesn't seem compatible with the Java Way.
on all platforms, values of rect.x+rect.width greater than the screen's width (and likewise for the height) cause the Robot to grab off-screen pixels.
i guess there are potentially security implications here, though an exploit would probably be quite hard to come by.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.event.*;
public class RobotBug extends JFrame {
private Robot robot;
private Timer timer;
private ScaledImagePanel scaledImagePanel;
private int scaleFactor;
public RobotBug() {
super("RobotBug");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
try {
robot = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
System.exit(0);
}
timer = new Timer(50, new MouseTracker());
setSize(new Dimension(250, 300));
setContentPane(makeUi());
timer.start();
}
private JComponent makeUi() {
JPanel result = new JPanel(new BorderLayout());
result.add(scaledImagePanel = new ScaledImagePanel(), BorderLayout.CENTER);
result.add(makeControlPanel(), BorderLayout.SOUTH);
return result;
}
private JComponent makeControlPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(makeScaleSlider(), BorderLayout.CENTER);
panel.add(makeShowGridCheckBox(), BorderLayout.EAST);
panel.setBorder(new javax.swing.border.EmptyBorder(0, 12, 4, 12));
return panel;
}
private JCheckBox makeShowGridCheckBox() {
final JCheckBox checkBox = new JCheckBox("Show Grid");
checkBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
scaledImagePanel.setShowGrid(checkBox.isSelected());
}
});
checkBox.setSelected(false);
return checkBox;
}
private JSlider makeScaleSlider() {
final JSlider scaleSlider = new JSlider(1, 4);
scaleSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
scaleFactor = (1 << scaleSlider.getValue());
repaint();
}
});
Hashtable<Integer, JComponent> labels = new Hashtable<Integer, JComponent>();
for (int i = scaleSlider.getMinimum(); i <= scaleSlider.getMaximum(); ++i) {
labels.put(i, new JLabel(Integer.toString(1 << i) + "x"));
}
scaleSlider.setLabelTable(labels);
scaleSlider.setPaintLabels(true);
scaleSlider.setPaintTicks(true);
scaleSlider.setSnapToTicks(true);
scaleSlider.setValue(1);
return scaleSlider;
}
private class ScaledImagePanel extends JComponent {
private Image image;
private boolean showGrid;
public void setImage(Image image) {
this.image = image;
repaint();
}
public void setShowGrid(boolean showGrid) {
this.showGrid = showGrid;
repaint();
}
public void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
paintGridLines(g);
}
private void paintGridLines(Graphics g) {
if (showGrid == false) {
return;
}
g.setColor(Color.BLACK);
for (int x = scaleFactor; x < getWidth(); x += scaleFactor) {
g.drawLine(x, 0, x, getHeight());
}
for (int y = scaleFactor; y < getHeight(); y += scaleFactor) {
g.drawLine(0, y, getWidth(), y);
}
}
}
private class MouseTracker implements ActionListener {
private Point lastPosition = null;
public void actionPerformed(ActionEvent e) {
if (scaledImagePanel.isShowing() == false) {
return;
}
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
Point center = pointerInfo.getLocation();
if (lastPosition != null && lastPosition.equals(center)) {
return;
}
lastPosition = center;
Rectangle screenCaptureBounds = getScreenCaptureBounds(center);
BufferedImage capturedImage = robot.createScreenCapture(screenCaptureBounds);
Image scaledImage = capturedImage.getScaledInstance(scaledImagePanel.getWidth(), scaledImagePanel.getHeight(), Image.SCALE_REPLICATE);
scaledImagePanel.setImage(scaledImage);
}
private Rectangle getScreenCaptureBounds(Point center) {
Point topLeft = new Point(center.x - scaledImagePanel.getWidth() / (2 * scaleFactor), center.y - scaledImagePanel.getHeight() / (2 * scaleFactor));
Rectangle result = new Rectangle(topLeft, scaledImagePanel.getSize());
result.width /= scaleFactor;
result.height /= scaleFactor;
// Constrain the capture to the display.
// Apple's 1.5 VM crashes if you don't.
// FIXME
// result.x = Math.max(result.x, 0);
// result.y = Math.max(result.y, 0);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if (result.x + result.width > screenSize.width) {
result.x = screenSize.width - result.width;
}
if (result.y + result.height > screenSize.height) {
result.y = screenSize.height - result.height;
}
return result;
}
}
public static void main(String[] args) {
new RobotBug().setVisible(true);
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
near the edge of the screen, with the lines commented out near "FIXME", i'd expect an exception to be thrown because of the invalid rectangle.
ACTUAL -
weird funky effects as i get to see off-screen display memory.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
see above!
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
don't pass a Rectangle that asks for off-screen pixels.
java version "1.5.0_03"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_03-b07)
Java HotSpot(TM) Client VM (build 1.5.0_03-b07, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Linux bertha 2.6.8-1-686-smp #1 SMP Thu Nov 25 04:55:00 UTC 2004 i686 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
Robot contains the following check on Rectangles passed to createScreenCapture:
private static void checkValidRect(Rectangle rect) {
if (rect.width <= 0 || rect.height <= 0) {
throw new IllegalArgumentException("Rectangle width and height must be > 0");
}
}
on Mac OS, if rect.y < 0, the Java VM crashes.
on all platforms, values of rect.x or rect.y less than 0 give undefined results, which doesn't seem compatible with the Java Way.
on all platforms, values of rect.x+rect.width greater than the screen's width (and likewise for the height) cause the Robot to grab off-screen pixels.
i guess there are potentially security implications here, though an exploit would probably be quite hard to come by.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.event.*;
public class RobotBug extends JFrame {
private Robot robot;
private Timer timer;
private ScaledImagePanel scaledImagePanel;
private int scaleFactor;
public RobotBug() {
super("RobotBug");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
try {
robot = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
System.exit(0);
}
timer = new Timer(50, new MouseTracker());
setSize(new Dimension(250, 300));
setContentPane(makeUi());
timer.start();
}
private JComponent makeUi() {
JPanel result = new JPanel(new BorderLayout());
result.add(scaledImagePanel = new ScaledImagePanel(), BorderLayout.CENTER);
result.add(makeControlPanel(), BorderLayout.SOUTH);
return result;
}
private JComponent makeControlPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(makeScaleSlider(), BorderLayout.CENTER);
panel.add(makeShowGridCheckBox(), BorderLayout.EAST);
panel.setBorder(new javax.swing.border.EmptyBorder(0, 12, 4, 12));
return panel;
}
private JCheckBox makeShowGridCheckBox() {
final JCheckBox checkBox = new JCheckBox("Show Grid");
checkBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
scaledImagePanel.setShowGrid(checkBox.isSelected());
}
});
checkBox.setSelected(false);
return checkBox;
}
private JSlider makeScaleSlider() {
final JSlider scaleSlider = new JSlider(1, 4);
scaleSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
scaleFactor = (1 << scaleSlider.getValue());
repaint();
}
});
Hashtable<Integer, JComponent> labels = new Hashtable<Integer, JComponent>();
for (int i = scaleSlider.getMinimum(); i <= scaleSlider.getMaximum(); ++i) {
labels.put(i, new JLabel(Integer.toString(1 << i) + "x"));
}
scaleSlider.setLabelTable(labels);
scaleSlider.setPaintLabels(true);
scaleSlider.setPaintTicks(true);
scaleSlider.setSnapToTicks(true);
scaleSlider.setValue(1);
return scaleSlider;
}
private class ScaledImagePanel extends JComponent {
private Image image;
private boolean showGrid;
public void setImage(Image image) {
this.image = image;
repaint();
}
public void setShowGrid(boolean showGrid) {
this.showGrid = showGrid;
repaint();
}
public void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
paintGridLines(g);
}
private void paintGridLines(Graphics g) {
if (showGrid == false) {
return;
}
g.setColor(Color.BLACK);
for (int x = scaleFactor; x < getWidth(); x += scaleFactor) {
g.drawLine(x, 0, x, getHeight());
}
for (int y = scaleFactor; y < getHeight(); y += scaleFactor) {
g.drawLine(0, y, getWidth(), y);
}
}
}
private class MouseTracker implements ActionListener {
private Point lastPosition = null;
public void actionPerformed(ActionEvent e) {
if (scaledImagePanel.isShowing() == false) {
return;
}
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
Point center = pointerInfo.getLocation();
if (lastPosition != null && lastPosition.equals(center)) {
return;
}
lastPosition = center;
Rectangle screenCaptureBounds = getScreenCaptureBounds(center);
BufferedImage capturedImage = robot.createScreenCapture(screenCaptureBounds);
Image scaledImage = capturedImage.getScaledInstance(scaledImagePanel.getWidth(), scaledImagePanel.getHeight(), Image.SCALE_REPLICATE);
scaledImagePanel.setImage(scaledImage);
}
private Rectangle getScreenCaptureBounds(Point center) {
Point topLeft = new Point(center.x - scaledImagePanel.getWidth() / (2 * scaleFactor), center.y - scaledImagePanel.getHeight() / (2 * scaleFactor));
Rectangle result = new Rectangle(topLeft, scaledImagePanel.getSize());
result.width /= scaleFactor;
result.height /= scaleFactor;
// Constrain the capture to the display.
// Apple's 1.5 VM crashes if you don't.
// FIXME
// result.x = Math.max(result.x, 0);
// result.y = Math.max(result.y, 0);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if (result.x + result.width > screenSize.width) {
result.x = screenSize.width - result.width;
}
if (result.y + result.height > screenSize.height) {
result.y = screenSize.height - result.height;
}
return result;
}
}
public static void main(String[] args) {
new RobotBug().setVisible(true);
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
near the edge of the screen, with the lines commented out near "FIXME", i'd expect an exception to be thrown because of the invalid rectangle.
ACTUAL -
weird funky effects as i get to see off-screen display memory.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
see above!
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
don't pass a Rectangle that asks for off-screen pixels.